diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f61c5c914c24..5d76a4efec936 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,8 +155,12 @@ jobs: - name: Tests id: test run: npx hereby test:tsc + env: + TMPDIR: ${{ runner.temp }} - name: Benchmarks run: npx hereby test:benchmarks + env: + TMPDIR: ${{ runner.temp }} - run: git add . - uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 if: ${{ always() && matrix.config.coverage && github.event_name != 'merge_group' }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 462f0e44e9fef..8a405113a8e1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -102,6 +102,7 @@ The repository uses a Go workspace with modules in `tsc/` and `tools/`. npx hereby build # Build the native compiler into built/local/tsc npx hereby test # Run compiler and language-service Go tests npx hereby test:all # Also run benchmarks, tools, and API tests +npx hereby test:codegen # Run codegen tests without regenerating source outputs npx hereby lint # Run custom golangci-lint for both Go modules npx hereby generate:go # Run the generators also exposed through go generate npx hereby generate # Regenerate all sources, assets, localization, and vendored files @@ -116,6 +117,14 @@ Generation is owned by `Herebyfile.mjs`; Go directives forward to the same to bypass incremental caches. `generate` includes every generator and fetches the pinned LSP model when its local cache is missing or stale. Use `generate:go` for the narrower scope of `go generate ./...` in the `tsc` module. +Up-to-date generators emit no status messages. + +`npx hereby build` runs `generate` before compiling; a separate generation step +is not needed. The API and package build tasks share this prerequisite, and +`build:watch` runs the full incremental generation suite before every rebuild. +Each watch pass clears cached input fingerprints and runs generation in-process. +Restart the watcher after editing generator implementations. Generator-owned outputs are +excluded from watch events to avoid regeneration loops. Package-specific commands: @@ -142,19 +151,8 @@ go -C ./tsc test -run='TestLocal/' ./internal/testrunner Run: ```bash -npx hereby generate -npx hereby build -npx hereby test -npx hereby test:all -npx hereby lint -npx hereby format -npx hereby check:format -npm run -w @typescript/typescript build -npm run -w @typescript/typescript test -npm run -w native-preview build -go -C ./tsc mod tidy -diff -go -C ./tools mod tidy -diff -go work sync +npx hereby validate --all +npx hereby tidy git diff --exit-code ``` diff --git a/Herebyfile.mjs b/Herebyfile.mjs index bf3010588e80a..3ecad38769fd5 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -2,7 +2,7 @@ import AdmZip from "adm-zip"; import chokidar from "chokidar"; -import { task } from "hereby"; +import { task as herebyTask } from "hereby"; import assert from "node:assert"; import crypto from "node:crypto"; import fs from "node:fs"; @@ -15,11 +15,24 @@ import { styleText, } from "node:util"; import * as tar from "tar"; -import { xSync } from "tinyexec"; +import { + x, + xSync, +} from "tinyexec"; import { enableFileFingerprintCache, + invalidateFileFingerprints, run, } from "./tools/scripts/gen/utils.mts"; +import { + ensureModel as ensureLSPModel, + outputs as lspModelOutputs, +} from "./tools/scripts/lsp/fetchModel.mts"; +import { outputs as encoderOutputs } from "./tools/scripts/tsc/generate-encoder.ts"; +import { outputs as enumOutputs } from "./tools/scripts/tsc/generate-enums.ts"; +import { outputs as goASTOutputs } from "./tools/scripts/tsc/generate-go-ast.ts"; +import { outputs as tsASTOutputs } from "./tools/scripts/tsc/generate-ts-ast.ts"; +import generateUnicodeData, { outputs as unicodeOutputs } from "./tsc/internal/stringutil/_scripts/generate-unicode-data.mts"; enableFileFingerprintCache(); @@ -30,6 +43,17 @@ if (process.platform === "win32") { const __filename = url.fileURLToPath(new URL(import.meta.url)); const __dirname = path.dirname(__filename); +/** @typedef {import("hereby").TaskOptions & { outputs?: readonly string[] }} TaskOptions */ +/** @type {TaskOptions[]} */ +const taskDescriptors = []; + +/** @param {TaskOptions} options */ +function task(options) { + const result = herebyTask(options); + taskDescriptors.push(options); + return result; +} + const isCI = !!process.env.CI || !!process.env.TF_BUILD; const stableThreeComponentVersionPatternSource = String.raw`^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$`; const stableThreeComponentVersionPattern = new RegExp(stableThreeComponentVersionPatternSource); @@ -219,7 +243,6 @@ async function runWithConcurrencyLimit(tasks, concurrency) { const builtLocal = "./built/local"; const libsDir = "./tsc/internal/bundled/libs"; -const libsRegexp = /(?:^|[\\/])internal[\\/]bundled[\\/]libs[\\/]/; /** * @param {string} out @@ -230,7 +253,7 @@ async function generateLibs(out) { const libs = await fs.promises.readdir(libsDir); await Promise.all(libs.map(async lib => { - fs.promises.copyFile(path.join(libsDir, lib), path.join(out, lib)); + await fs.promises.copyFile(path.join(libsDir, lib), path.join(out, lib)); })); } @@ -271,71 +294,6 @@ function buildTsc(opts) { }); } -export const tscBuild = task({ - name: "tsc:build", - description: "Builds the tsc binary.", - run: async () => { - await buildTsc({ extraFlags: options.release ? getReleaseBuildFlags() : [] }); - }, -}); - -export const tsgo = task({ - name: "tsgo", - dependencies: [lib, tscBuild], -}); - -export const local = task({ - name: "local", - dependencies: [tsgo], -}); - -export const build = task({ - name: "build", - dependencies: [local], -}); - -export const buildWatch = task({ - name: "build:watch", - description: "Builds the tsc binary and watches for changes.", - run: async () => { - await watchDebounced("build:watch", async (paths, abortSignal) => { - let libsChanged = false; - let goChanged = false; - - if (paths) { - for (const p of paths) { - if (libsRegexp.test(p)) { - libsChanged = true; - } - else if (p.endsWith(".go")) { - goChanged = true; - } - if (libsChanged && goChanged) { - break; - } - } - } - else { - libsChanged = true; - goChanged = true; - } - - if (libsChanged) { - console.log("Generating libs..."); - await generateLibs(builtLocal); - } - - if (goChanged) { - console.log("Building tsgo..."); - await buildTsc({ abortSignal }); - } - }, { - paths: ["tsc/cmd", "tsc/internal"], - ignored: path => /[\\/]testdata[\\/]/.test(path), - }); - }, -}); - export const cleanBuilt = task({ name: "clean:built", hiddenFromTaskList: true, @@ -361,7 +319,10 @@ export const generateGo = task({ }); const getGoGenerateEnvironment = memoize(async () => { - const { stdout } = await runOutput("go", ["env", "-json", "GOOS", "GOARCH", "GOROOT"], { cwd: "./tsc" }); + const { stdout } = await x("go", ["env", "-json", "GOOS", "GOARCH", "GOROOT"], { + throwOnError: true, + nodeOptions: { cwd: "./tsc" }, + }); return /** @type {{ GOOS: string; GOARCH: string; GOROOT: string }} */ (JSON.parse(stdout)); }); @@ -409,8 +370,9 @@ async function runGoGenerator(name, { file, ...spec }) { /** * @param {string} name * @param {GoGenerator[] | (() => Promise)} generators + * @param {readonly string[]} [extraOutputs] */ -function goGenerateTask(name, generators) { +function goGenerateTask(name, generators, extraOutputs = []) { const run = typeof generators === "function" ? generators : async () => { for (const generator of generators) await runGoGenerator(name, generator); }; @@ -418,6 +380,10 @@ function goGenerateTask(name, generators) { return task({ name, description: `Generates ${name.slice("generate:".length)} files. Pass --force to regenerate unchanged files.`, + outputs: [ + ...(typeof generators === "function" ? [] : generators.flatMap(generator => generator.outputs.map(output => path.resolve(__dirname, generator.cwd ?? path.dirname(generator.file), output)))), + ...extraOutputs, + ], run, }); } @@ -460,11 +426,13 @@ function moqGenerator(file, type, output, { source = ".", packageName, inputs = }; } +const astStringerGenerator = stringerGenerator("tsc/internal/ast/kind_generated.go", "Kind", "kind_stringer_generated.go"); + async function runGenerateASTStringer() { - await runGoGenerator("generate:ast-stringer", stringerGenerator("tsc/internal/ast/kind_generated.go", "Kind", "kind_stringer_generated.go")); + await runGoGenerator("generate:ast-stringer", astStringerGenerator); } -export const generateASTStringer = goGenerateTask("generate:ast-stringer", runGenerateASTStringer); +export const generateASTStringer = goGenerateTask("generate:ast-stringer", [astStringerGenerator]); export const generateBundled = goGenerateTask("generate:bundled", [{ file: "tsc/internal/bundled/bundled.go", @@ -492,6 +460,19 @@ goGenerateActions.push(runGenerateCompilerOptions); export const generateCompilerOptions = task({ name: "generate:compileroptions", description: "Generates compiler options and their dependent API files. Pass --force to regenerate unchanged files.", + outputs: [ + "packages/typescript/src/api/compilerOptions.generated.ts", + "packages/typescript/src/api/prepareCompilerOptions.generated.ts", + "packages/typescript/schemas/tsconfig.schema.json", + "packages/typescript/schemas/jsconfig.schema.json", + "tsc/internal/core/options_generated.go", + "tsc/internal/core/modulekind_stringer_generated.go", + "tsc/internal/core/scripttarget_stringer_generated.go", + "tsc/internal/transpile/options_generated.go", + "tsc/internal/tsoptions/declarations_generated.go", + "tsc/internal/tsoptions/options_generated.go", + "tsc/internal/testrunner/options_generated.go", + ], run: async () => { await runGenerateCompilerOptions(); await runGenerateEnums(); @@ -516,7 +497,7 @@ export const generateDiagnostics = goGenerateTask("generate:diagnostics", [ file: "tsc/internal/diagnostics/diagnostics.go", inputs: ["generate.go", "diagnosticMessages.json", "../../../tools/LocProject.json", "../{collections,json}/*.go", "loc/*.generated.json"], exclude: ["**/*_test.go"], - outputs: ["diagnostics_generated.go", "diagnosticMessages.generated.json", "loc_generated.go", "loc/*.json.gz"], + outputs: ["diagnostics_generated.go", "loc_generated.go", "diagnosticMessages.generated.json", "loc/*.json.gz"], commands: [ [ "go", @@ -537,7 +518,7 @@ export const generateDiagnostics = goGenerateTask("generate:diagnostics", [ ], }, stringerGenerator("tsc/internal/diagnostics/diagnostics.go", "Category", "stringer_generated.go"), -]); +], ["tsc/internal/diagnostics/loc"]); export const generateAutoImport = goGenerateTask("generate:autoimport", [ stringerGenerator("tsc/internal/ls/autoimport/export.go", "ExportSyntax", "export_stringer_generated.go"), @@ -571,16 +552,18 @@ export const generateVFSMatch = goGenerateTask("generate:vfsmatch", [ ]); export const generateUnicode = goGenerateTask("generate:unicode", async () => { - const { default: generate } = await import("./tsc/internal/stringutil/_scripts/generate-unicode-data.mts"); - await generate(!!options.force); -}); + await generateUnicodeData(!!options.force); +}, unicodeOutputs.map(output => path.join("tsc/internal/stringutil", output))); + +const extensionOutputs = ["packages/vscode-typescript/l10n/bundle.l10n.json"]; +const extensionTestOutputs = ["packages/vscode-typescript/l10n/bundle.l10n.qps-ploc.json", "packages/vscode-typescript/package.nls.qps-ploc.json"]; async function runGenerateExtension() { const { default: cache } = await import("./tools/scripts/gen/cache.mts"); await cache({ cwd: __dirname, inputs: [__filename, "tools/scripts/gen/extensionLocalization.mts", "packages/vscode-typescript/package.json", "packages/vscode-typescript/src/**/*"], - outputs: ["packages/vscode-typescript/l10n/bundle.l10n.json"], + outputs: extensionOutputs, commands: [["npm", "run", "-w", "native-preview", "generateLocBundle"]], envInputs: [], force: !!options.force, @@ -590,6 +573,7 @@ async function runGenerateExtension() { export const generateExtension = task({ name: "generate:extension", description: "Generates files in the extension. Pass --force to regenerate unchanged files.", + outputs: extensionOutputs, run: runGenerateExtension, }); @@ -598,7 +582,7 @@ async function runGenerateExtensionTest() { await cache({ cwd: __dirname, inputs: [__filename, "tools/scripts/gen/extensionLocalization.mts", "packages/vscode-typescript/package.json", "packages/vscode-typescript/l10n/bundle.l10n.json", "packages/vscode-typescript/package.nls.json"], - outputs: ["packages/vscode-typescript/l10n/bundle.l10n.qps-ploc.json", "packages/vscode-typescript/package.nls.qps-ploc.json"], + outputs: extensionTestOutputs, commands: [["npm", "run", "-w", "native-preview", "generateLocTest"]], envInputs: [], force: !!options.force, @@ -609,20 +593,17 @@ export const generateExtensionTest = task({ name: "generate:extension-test", description: "Generates pseudo-localized extension resources. Pass --force to regenerate unchanged files.", dependencies: [generateExtension], + outputs: extensionTestOutputs, run: runGenerateExtensionTest, }); +const lspOutputs = ["tsc/internal/lsp/lsproto/lsp_generated.go", "packages/typescript/src/vscode/protocol.generated.ts"]; + async function runGenerateLSP() { const { GeneratedFile } = await import("./tools/scripts/gen/generatedFile.mts"); const directory = path.join(__dirname, "tools/scripts/lsp"); - const modelFiles = ["metaModel.json", "metaModelSchema.mts"].map(file => new GeneratedFile(path.join(directory, file), [path.join(directory, "fetchModel.mts"), path.join(__dirname, "package-lock.json")])); - if (!modelFiles.every(file => file.isCurrent(!!options.force))) { - for (const file of modelFiles) file.invalidate(); - const { default: fetchModel } = await import("./tools/scripts/lsp/fetchModel.mts"); - await fetchModel(); - for (const file of modelFiles) file.markCurrent(); - } - const output = new GeneratedFile(path.join(__dirname, "tsc/internal/lsp/lsproto/lsp_generated.go"), [ + const modelFiles = await ensureLSPModel(!!options.force); + const output = new GeneratedFile(path.join(__dirname, lspOutputs[0]), [ __filename, path.join(directory, "generate.mts"), ...modelFiles.map(file => file.fileName), @@ -633,10 +614,7 @@ async function runGenerateLSP() { await generate(); output.markCurrent(); } - else { - console.log("LSP bindings are up to date."); - } - const typeScriptOutput = new GeneratedFile(path.join(__dirname, "packages/typescript/src/vscode/protocol.generated.ts"), [ + const typeScriptOutput = new GeneratedFile(path.join(__dirname, lspOutputs[1]), [ __filename, path.join(directory, "generate.mts"), path.join(directory, "generateTypeScript.mts"), @@ -655,6 +633,7 @@ async function runGenerateLSP() { export const generateLSP = task({ name: "generate:lsp", description: "Generates Go LSP bindings and extension API types from the pinned protocol model. Pass --force to regenerate unchanged files.", + outputs: [...lspOutputs, ...lspModelOutputs.map(output => path.join("tools/scripts/lsp", output))], run: runGenerateLSP, }); @@ -666,6 +645,7 @@ async function runGenerateEnums() { export const generateEnums = task({ name: "generate:enums", description: "Generates TypeScript enums from metadata and Go source. Pass --force to regenerate unchanged files.", + outputs: enumOutputs, run: runGenerateEnums, }); @@ -678,6 +658,7 @@ async function runGenerateAST() { export const generateAST = task({ name: "generate:ast", description: "Generates AST, kind stringer, and encoder files from ast.json. Pass --force to regenerate unchanged files.", + outputs: [...goASTOutputs, ...tsASTOutputs, ...encoderOutputs], run: runGenerateAST, }); @@ -689,9 +670,16 @@ async function runGenerateSync() { export const generateSync = task({ name: "generate:sync", description: "Generates synchronous and generator APIs and tests. Pass --force to regenerate unchanged files.", + outputs: ["packages/typescript/src/api/sync", "packages/typescript/test/sync", "packages/typescript/test/generators/api.bench.ts"], run: runGenerateSync, }); +const apiOutputs = ["packages/typescript/src/api/proto.generated.ts"]; +const preferenceOutputs = [ + "tsc/internal/ls/lsutil/userpreferences_generated.go", + "packages/typescript/src/api/userPreferences.generated.ts", +]; + async function runGeneratePreferences() { await runGoGenerator("generate:preferences", { file: "tsc/internal/ls/lsutil/userpreferences.go", @@ -699,10 +687,7 @@ async function runGeneratePreferences() { inputs: ["tools/userPreferences.schema.json", "tools/gen-preferences/*.go"], exclude: ["**/*_test.go"], envInputs: [], - outputs: [ - "tsc/internal/ls/lsutil/userpreferences_generated.go", - "packages/typescript/src/api/userPreferences.generated.ts", - ], + outputs: preferenceOutputs, commands: [ ["go", "-C", "./tools", "run", "./gen-preferences", "./userPreferences.schema.json", "../tsc/internal/ls/lsutil/userpreferences_generated.go", "../packages/typescript/src/api/userPreferences.generated.ts"], ["dprint", "fmt", "tsc/internal/ls/lsutil/userpreferences_generated.go", "packages/typescript/src/api/userPreferences.generated.ts"], @@ -710,7 +695,7 @@ async function runGeneratePreferences() { }); } -export const generatePreferences = goGenerateTask("generate:preferences", runGeneratePreferences); +export const generatePreferences = goGenerateTask("generate:preferences", runGeneratePreferences, preferenceOutputs); async function runGenerateAPI() { await runGeneratePreferences(); @@ -728,7 +713,7 @@ async function runGenerateAPI() { ], exclude: ["**/*_test.go"], envInputs: [], - outputs: ["packages/typescript/src/api/proto.generated.ts"], + outputs: apiOutputs, commands: [ ["go", "-C", "./tools", "run", "./gen-proto", "../tsc/internal/api/proto.go", "../packages/typescript/src/api/proto.generated.ts"], ["dprint", "fmt", "packages/typescript/src/api/proto.generated.ts"], @@ -739,6 +724,7 @@ async function runGenerateAPI() { export const generateAPI = task({ name: "generate:api", description: "Generates API files. Pass --force to regenerate unchanged files.", + outputs: apiOutputs, run: runGenerateAPI, }); @@ -758,7 +744,6 @@ async function runGenerateVendor() { } const manifest = new GeneratedFile(path.join(dest, "package.json"), [__filename, path.join(src, "package.json")]); if (manifest.isCurrent(!!options.force)) { - console.log("Vendored vscode-jsonrpc files are up to date."); return; } manifest.invalidate(); @@ -773,20 +758,108 @@ async function runGenerateVendor() { export const generateVendor = task({ name: "generate:vendor", description: "Updates the vendored copy of vscode-jsonrpc from node_modules. Pass --force to regenerate unchanged files.", + outputs: [vendorJsonrpcDir], run: runGenerateVendor, }); -const generateCompiler = task({ - name: "generate:compiler", - hiddenFromTaskList: true, - dependencies: [generateAST, generateLSP], - run: runGenerateGo, -}); +async function runGenerate() { + await runWithConcurrencyLimit([ + async () => { + await runWithConcurrencyLimit([runGenerateAST, runGenerateLSP], 2); + await runGenerateGo(); + }, + runGenerateSync, + async () => { + await runGenerateExtension(); + await runGenerateExtensionTest(); + }, + runGenerateVendor, + ], 4); +} export const generate = task({ name: "generate", description: "Runs all code generation, including AST, LSP, APIs, extension localization, and vendored dependencies.", - dependencies: [generateCompiler, generateSync, generateExtensionTest, generateVendor], + run: runGenerate, +}); + +export const tscBuild = task({ + name: "tsc:build", + description: "Generates sources and builds the tsc binary.", + dependencies: [generate], + run: async () => { + await buildTsc({ extraFlags: options.release ? getReleaseBuildFlags() : [] }); + }, +}); + +export const tsgo = task({ + name: "tsgo", + dependencies: [lib, tscBuild], +}); + +export const local = task({ + name: "local", + dependencies: [tsgo], +}); + +export const build = task({ + name: "build", + dependencies: [local], +}); + +function getGeneratedOutputPaths() { + const outputs = taskDescriptors.flatMap(descriptor => descriptor.outputs ?? []); + return new Set([...outputs, ...fs.globSync(outputs, { cwd: __dirname })].map(output => path.resolve(__dirname, output))); +} + +export const buildWatch = task({ + name: "build:watch", + description: "Watches sources and generator inputs, regenerating and rebuilding the tsc binary on changes.", + run: async () => { + const generatedOutputs = getGeneratedOutputPaths(); + const generatorDirectories = [ + "tsc/internal/stringutil/_scripts", + ].map(directory => path.resolve(directory)); + await watchDebounced("build:watch", async (_paths, abortSignal) => { + abortSignal.throwIfAborted(); + invalidateFileFingerprints(); + await runGenerate(); + abortSignal.throwIfAborted(); + await generateLibs(builtLocal); + abortSignal.throwIfAborted(); + console.log("Building tsgo..."); + await buildTsc({ abortSignal }); + }, { + paths: [ + "tsc/cmd", + "tsc/internal", + "tools/scripts/tsc/ast.json", + "tools/gen-proto", + "tools/gen-preferences", + "tools/userPreferences.schema.json", + "packages/typescript/src/api/async", + "packages/typescript/test/async", + "packages/vscode-typescript/src", + "packages/vscode-typescript/package.json", + "packages/vscode-typescript/package.nls.json", + "node_modules/vscode-jsonrpc/package.json", + "package-lock.json", + ".dprint.jsonc", + "go.work", + "go.work.sum", + "tsc/go.mod", + "tsc/go.sum", + "tools/go.mod", + "tools/go.sum", + ], + ignored: file => { + const absolutePath = path.resolve(file); + return generatedOutputs.has(absolutePath) + || generatorDirectories.some(directory => absolutePath === directory || absolutePath.startsWith(directory + path.sep)) + || path.normalize(file).split(path.sep).includes("testdata"); + }, + }); + }, }); const coverageDir = path.join(__dirname, "coverage"); @@ -1023,9 +1096,8 @@ export const testTools = task({ export const testCodegen = task({ name: "test:codegen", - description: "Runs incremental codegen tests.", + description: "Runs codegen tests without regenerating sources.", run: async () => { - await run("go", ["-C", "tsc", "mod", "download"]); await run("node", ["--test", "--test-concurrency=1", "./tools/scripts/gen/*.test.mts", "./tools/scripts/tsc/*.test.ts"]); }, }); @@ -1033,6 +1105,7 @@ export const testCodegen = task({ export const buildAPI = task({ name: "build:api", description: "Builds @typescript/typescript JS API.", + dependencies: [generate], run: async () => { await run("npm", ["run", "-w", "@typescript/typescript", "build"]); }, @@ -1049,15 +1122,14 @@ export const checkAPIDeclarations = task({ run: runCheckAPIDeclarations, }); -async function runBuildAPITests(generateSources = true) { - if (generateSources) await runGenerateSync(); +async function runBuildAPITests() { await run("npm", ["run", "-w", "@typescript/typescript", "build:test"]); } export const buildAPITests = task({ name: "build:api:test", description: "Builds the @typescript/typescript JS API tests.", - dependencies: [generateEnums, generateAPI], + dependencies: [generate], run: runBuildAPITests, }); @@ -1215,12 +1287,9 @@ async function runFormat() { export const validate = task({ name: "validate", - description: "Generates, builds, tests, lints, and formats the repo. Pass --api to include API tests, or --all to include all code generation and ancillary repository tests Benchmarks are separate: test:benchmarks and test:benchmarks:api.", - dependencies: [options.all ? generate : generateGo], + description: "Generates, builds, tests, lints, and formats the repo. Pass --api to include API tests, or --all to include API and ancillary repository tests. Benchmarks are separate: test:benchmarks and test:benchmarks:api.", + dependencies: [build], run: async () => { - await generateLibs(builtLocal); - await buildTsc({ extraFlags: options.release ? getReleaseBuildFlags() : [] }); - /** @type {{ name: string; error: unknown }[]} */ const failures = []; /** @param {string} name @param {() => Promise} action */ @@ -1238,8 +1307,7 @@ export const validate = task({ await runValidation("test:extension", runTestExtension); await runValidation("check:api-declarations", runCheckAPIDeclarations); if (options.api || options.all) { - if (!options.all) await runGenerateEnums(); - await runBuildAPITests(!options.all); + await runBuildAPITests(); await runValidation("test:api", runTestAPI); } if (options.all) { @@ -2267,6 +2335,7 @@ function stripConditionsFromValue(value) { export const buildNativePreviewPackages = task({ name: "typescript:build", description: "Builds TypeScript npm packages for the current platform. Pass --respectGoEnv to preserve caller-provided Go build settings.", + dependencies: usePublishedPlatformPackagesForVsix ? undefined : [generate], run: runBuildNativePreviewPackages, }); @@ -3129,6 +3198,7 @@ export const vscodeTypescriptRelease = task({ export const nativePreviewRelease = task({ name: "typescript:release", hiddenFromTaskList: true, + dependencies: [generate], run: async () => { if (releaseVscodeTypescript) { throw new Error("typescript:release cannot be used with --vscodeTypescriptRelease; use vscode-typescript:release"); diff --git a/packages/typescript/enums/nodeFlags.d.ts b/packages/typescript/enums/nodeFlags.d.ts index b9c3335f7603f..f03f6c24ccc76 100644 --- a/packages/typescript/enums/nodeFlags.d.ts +++ b/packages/typescript/enums/nodeFlags.d.ts @@ -31,6 +31,7 @@ export declare enum NodeFlags { PossiblyContainsDeprecatedTag = 1 << 26, Unreachable = 1 << 27, ReparserTransformedLiteral = 1 << 28, + CondFlowClean = 1 << 29, BlockScoped = Let | Const | Using, Constant = Const | Using, AwaitUsing = Const | Using, diff --git a/packages/typescript/enums/nodeFlags.js b/packages/typescript/enums/nodeFlags.js index 14fc1251943d3..012347c6a601d 100644 --- a/packages/typescript/enums/nodeFlags.js +++ b/packages/typescript/enums/nodeFlags.js @@ -31,6 +31,7 @@ export var NodeFlags; NodeFlags[NodeFlags["PossiblyContainsDeprecatedTag"] = 67108864] = "PossiblyContainsDeprecatedTag"; NodeFlags[NodeFlags["Unreachable"] = 134217728] = "Unreachable"; NodeFlags[NodeFlags["ReparserTransformedLiteral"] = 268435456] = "ReparserTransformedLiteral"; + NodeFlags[NodeFlags["CondFlowClean"] = 536870912] = "CondFlowClean"; NodeFlags[NodeFlags["BlockScoped"] = 7] = "BlockScoped"; NodeFlags[NodeFlags["Constant"] = 6] = "Constant"; NodeFlags[NodeFlags["AwaitUsing"] = 6] = "AwaitUsing"; diff --git a/packages/typescript/enums/objectFlags.d.ts b/packages/typescript/enums/objectFlags.d.ts index fd17f37c6fc0b..e51db9eca2ca7 100644 --- a/packages/typescript/enums/objectFlags.d.ts +++ b/packages/typescript/enums/objectFlags.d.ts @@ -37,6 +37,8 @@ export declare enum ObjectFlags { IdenticalBaseTypeCalculated = 1 << 27, IdenticalBaseTypeExists = 1 << 28, FromTypeNode = 1 << 29, + HasGenericArgumentsComputed = 1 << 30, + HasGenericArguments = 1 << 31, IsGenericTypeComputed = 1 << 22, IsGenericObjectType = 1 << 23, IsGenericIndexType = 1 << 24, diff --git a/packages/typescript/enums/objectFlags.js b/packages/typescript/enums/objectFlags.js index 013bee411f001..7470856e3abcb 100644 --- a/packages/typescript/enums/objectFlags.js +++ b/packages/typescript/enums/objectFlags.js @@ -37,6 +37,8 @@ export var ObjectFlags; ObjectFlags[ObjectFlags["IdenticalBaseTypeCalculated"] = 134217728] = "IdenticalBaseTypeCalculated"; ObjectFlags[ObjectFlags["IdenticalBaseTypeExists"] = 268435456] = "IdenticalBaseTypeExists"; ObjectFlags[ObjectFlags["FromTypeNode"] = 536870912] = "FromTypeNode"; + ObjectFlags[ObjectFlags["HasGenericArgumentsComputed"] = 1073741824] = "HasGenericArgumentsComputed"; + ObjectFlags[ObjectFlags["HasGenericArguments"] = -2147483648] = "HasGenericArguments"; ObjectFlags[ObjectFlags["IsGenericTypeComputed"] = 4194304] = "IsGenericTypeComputed"; ObjectFlags[ObjectFlags["IsGenericObjectType"] = 8388608] = "IsGenericObjectType"; ObjectFlags[ObjectFlags["IsGenericIndexType"] = 16777216] = "IsGenericIndexType"; diff --git a/packages/typescript/enums/symbolFlags.d.ts b/packages/typescript/enums/symbolFlags.d.ts index 18d3a7fcf1948..7dc98a0b431db 100644 --- a/packages/typescript/enums/symbolFlags.d.ts +++ b/packages/typescript/enums/symbolFlags.d.ts @@ -33,6 +33,7 @@ export declare enum SymbolFlags { ConstEnumOnlyModule = 1 << 28, ReplaceableByMethod = 1 << 29, GlobalLookup = 1 << 30, + NameProxy = 1 << 30, All = (1 << 30) - 1, Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, diff --git a/packages/typescript/enums/symbolFlags.js b/packages/typescript/enums/symbolFlags.js index 8c1e2ae54e2c8..905623522d128 100644 --- a/packages/typescript/enums/symbolFlags.js +++ b/packages/typescript/enums/symbolFlags.js @@ -33,6 +33,7 @@ export var SymbolFlags; SymbolFlags[SymbolFlags["ConstEnumOnlyModule"] = 268435456] = "ConstEnumOnlyModule"; SymbolFlags[SymbolFlags["ReplaceableByMethod"] = 536870912] = "ReplaceableByMethod"; SymbolFlags[SymbolFlags["GlobalLookup"] = 1073741824] = "GlobalLookup"; + SymbolFlags[SymbolFlags["NameProxy"] = 1073741824] = "NameProxy"; SymbolFlags[SymbolFlags["All"] = 1073741823] = "All"; SymbolFlags[SymbolFlags["Enum"] = 384] = "Enum"; SymbolFlags[SymbolFlags["Variable"] = 3] = "Variable"; diff --git a/packages/typescript/scripts/generateSync.ts b/packages/typescript/scripts/generateSync.ts index d236ea091ca78..a20dbdc531b4f 100755 --- a/packages/typescript/scripts/generateSync.ts +++ b/packages/typescript/scripts/generateSync.ts @@ -630,7 +630,6 @@ function getIndent(source: string, position: number): string { // ── Main ───────────────────────────────────────────────────────── export function generateSync(force = false): void { - console.log("Generating sync API from async source..."); const generatedFiles: (GeneratedFile | undefined)[] = []; // Source files @@ -676,7 +675,6 @@ export function generateSync(force = false): void { const changedFiles = generatedFiles.filter(file => file !== undefined); if (!changedFiles.length) { - console.log("Sync API is up to date."); return; } console.log("Formatting..."); diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index 64dbd13a61a75..29369a7392809 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -678,6 +678,13 @@ export class API implements FormatDiagnosticsHo private prepareCreateSnapshotParams(params: CreateSnapshotParams | undefined): ProtocolCreateSnapshotParams { params ??= {}; + if (params.fileSystem?.kind === "snapshot") { + const sourceId = params.fileSystem.snapshot; + const source = sourceId === undefined ? undefined : this.activeSnapshots.get(sourceId); + if (!source || source.isDisposed()) { + throw new Error("Cannot use an inactive snapshot filesystem"); + } + } const prepareOptions = (options: CreateProgramOptions | undefined): ProtocolCreateProgramOptions | undefined => { if (!options) return undefined; const { moduleResolver, projectReferences, ...rest } = options; @@ -1109,6 +1116,12 @@ export class Snapshot { return this.getProject(projectId)?.program; } + /** + * Creates a snapshot using this snapshot's projects and incremental state. + * Use `createFileSystemFromSnapshot(source)` from the filesystem API as `fileSystem` + * to overlay another snapshot's retained request entries without replaying + * intermediate updates. Changes and program reuse are relative to this snapshot. + */ update< const CreatePrograms extends CreateSnapshotParams["createPrograms"] = undefined, const OpenFiles extends CreateSnapshotParams["openFiles"] = undefined, diff --git a/packages/typescript/src/api/fs.ts b/packages/typescript/src/api/fs.ts index 25bfe9f044309..762711f4352ac 100644 --- a/packages/typescript/src/api/fs.ts +++ b/packages/typescript/src/api/fs.ts @@ -5,6 +5,7 @@ import type { RootedFilePath, RootedPath, } from "../ast/index.ts"; +import type { Snapshot } from "./async/api.ts"; import { normalizePath } from "./path.ts"; import type { RequestDirectoryEntries, @@ -169,6 +170,26 @@ export function createFileSystemLayer( return createRequestFileSystem("layer", files, options); } +/** + * Reuses a snapshot's retained request filesystem without resending its contents. + * Entries take precedence over the receiving snapshot's filesystem; a full source + * filesystem remains total. Host and editor contents are not copied. + * An optional layer or full filesystem is applied over the reused filesystem. + * The source must remain active until the snapshot operation completes. + */ +export function createFileSystemFromSnapshot( + snapshot: Pick, + fileSystem?: RequestFileSystem | undefined, +): RequestFileSystem { + if (snapshot.isDisposed()) { + throw new Error("Cannot create a filesystem from a disposed snapshot"); + } + if (fileSystem?.kind === "snapshot") { + throw new Error("Only a layer or full filesystem can be applied on top of a snapshot filesystem"); + } + return { kind: "snapshot", snapshot: snapshot.id, files: {}, fileSystem }; +} + function createRequestFileSystem( kind: RequestFileSystem["kind"], files: RequestFileEntries, diff --git a/packages/typescript/src/api/proto.generated.ts b/packages/typescript/src/api/proto.generated.ts index 6f49fab3ca6ab..c9ae4908adf5a 100644 --- a/packages/typescript/src/api/proto.generated.ts +++ b/packages/typescript/src/api/proto.generated.ts @@ -308,7 +308,9 @@ export interface CreateSnapshotParams extends SnapshotRequestChangesParams { /** * FileSystem supplies file contents and directory listings for the new snapshot. * A full filesystem is canonical and total. A filesystem layer is checked - * before falling back to the base snapshot or host filesystem. + * before falling back to the base snapshot or host filesystem. A snapshot + * filesystem overlays retained request entries from another active snapshot, + * then applies an optional layer or full filesystem. */ fileSystem?: RequestFileSystem | undefined; } @@ -1570,7 +1572,11 @@ export interface FileNotifications { * for a request that creates a snapshot. */ export interface RequestFileSystem { - kind: "full" | "layer"; + kind: "full" | "layer" | "snapshot"; + /** Snapshot identifies the retained snapshot supplying request entries for KindSnapshot. */ + snapshot?: number | undefined; + /** FileSystem is an optional layer or full filesystem applied on top of KindSnapshot. */ + fileSystem?: RequestFileSystem | undefined; /** Files maps file names to their complete contents. */ files: Record; /** @@ -1929,4 +1935,5 @@ export interface RawCompilerOptions { singleThreaded?: boolean | undefined; quiet?: boolean | undefined; checkers?: number | undefined; + maxMemory?: number | undefined; } diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index f08ef255288b9..e271dafabaf7b 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -1052,6 +1052,13 @@ export class API implements FormatDiagnosticsHo private prepareCreateSnapshotParams(params: CreateSnapshotParams | undefined): ProtocolCreateSnapshotParams { params ??= {}; + if (params.fileSystem?.kind === "snapshot") { + const sourceId = params.fileSystem.snapshot; + const source = sourceId === undefined ? undefined : this.activeSnapshots.get(sourceId); + if (!source || source.isDisposed()) { + throw new Error("Cannot use an inactive snapshot filesystem"); + } + } const prepareOptions = (options: CreateProgramOptions | undefined): ProtocolCreateProgramOptions | undefined => { if (!options) return undefined; const { moduleResolver, projectReferences, ...rest } = options; @@ -1737,6 +1744,12 @@ export class Snapshot { return this.getProject(projectId)?.program; } + /** + * Creates a snapshot using this snapshot's projects and incremental state. + * Use `createFileSystemFromSnapshot(source)` from the filesystem API as `fileSystem` + * to overlay another snapshot's retained request entries without replaying + * intermediate updates. Changes and program reuse are relative to this snapshot. + */ get update(): { (params: SnapshotOperationParams): SnapshotForOperationResults; (params: CreateSnapshotParams): Snapshot; diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index 379f07b6b68f7..9a37f0c38dfff 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -67,6 +67,7 @@ import { type CompilerOptions, type ConditionalType, type ConfiguredProjectId, + type CreateSnapshotParams, DiagnosticCategory, type DocumentIdentifier, EmitOnly, @@ -114,6 +115,7 @@ import { } from "@typescript/typescript/async"; // @sync: } from "@typescript/typescript/sync"; import { createFileSystem, + createFileSystemFromSnapshot, createFileSystemLayer, createFileSystemWithLib, type FileSystemCallbacks, @@ -2034,6 +2036,51 @@ describe("BuildOrchestrator", () => { }); describe("Checker - getImmediateAliasedSymbol", { concurrency }, () => { + for (const declaration of [`import * as X from "./mod";`, `export * as X from "./mod";`]) { + for (const moduleContent of [`export default class User {}`, `export class User {}`]) { + test(`preserves module symbol identity for ${declaration} with ${moduleContent}`, async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/mod.ts": moduleContent, + "/src/main.ts": declaration, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const checker = snapshot.getConfiguredProject("/tsconfig.json")!.checker; + const moduleSymbol = await checker.getSymbolOfSourceFile("/src/mod.ts"); + assert.ok(moduleSymbol); + const aliasSymbol = await checker.getSymbolAtPosition("/src/main.ts", declaration.indexOf("X")); + assert.ok(aliasSymbol); + assert.equal(await checker.getImmediateAliasedSymbol(aliasSymbol) === moduleSymbol, true); + assert.equal(await checker.getImmediateAliasedSymbol(aliasSymbol) === moduleSymbol, true); + }); + } + } + + test("preserves the export assignment alias of a namespace import", async () => { + const declaration = `import * as X from "./mod";`; + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/user.ts": `export class User {}`, + "/src/mod.ts": `import { User } from "./user"; export = User;`, + "/src/main.ts": declaration, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const checker = snapshot.getConfiguredProject("/tsconfig.json")!.checker; + const moduleSymbol = await checker.getSymbolOfSourceFile("/src/mod.ts"); + assert.ok(moduleSymbol); + const exportAssignment = (await moduleSymbol.getExports()).get(InternalSymbolName.ExportEquals); + assert.ok(exportAssignment); + const aliasSymbol = await checker.getSymbolAtPosition("/src/main.ts", declaration.indexOf("X")); + assert.ok(aliasSymbol); + assert.equal(await checker.getImmediateAliasedSymbol(aliasSymbol) === exportAssignment, true); + const resolved = await checker.getAliasedSymbol(aliasSymbol); + assert.ok(resolved); + assert.equal(resolved.name, "User"); + assert.ok(resolved.flags & SymbolFlags.Class); + }); + test("resolves one level of alias indirection", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5550,60 +5597,266 @@ describe("updateSnapshot file systems", { concurrency }, () => { assert.deepEqual(callbackCalls, []); }); - test("Snapshot.update layers filesystem edits and removals", async () => { - await using api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + test("createFileSystemFromSnapshot supports creating snapshots with additional layer and full filesystems", async () => { + await using api = spawnAPI({}); + await using source = await api.createSnapshot({ + fileSystem: createFileSystemLayer([ + ["/source.ts", "source"], + ["/removed/nested.ts", "removed"], + ]), }); - await using snapshot = await api.createSnapshot({ - openProjects: ["/tsconfig.json"], - fileSystem: createFileSystem(Object.entries({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { noLib: true }, - include: ["src/**/*.ts"], - }), - "/src/keep.ts": `export const keep = true;`, - "/src/change.ts": `export const version = "old";`, - "/src/remove.ts": `export const remove = true;`, - "/src/removed/gone.ts": `export const gone = true;`, - })), + const descriptor = createFileSystemFromSnapshot(source); + assert.equal(descriptor.kind, "snapshot"); + assert.equal(descriptor.snapshot, source.id); + assert.deepEqual(descriptor.files, {}); + await using cloned = await api.createSnapshot({ + fileSystem: descriptor, + createPrograms: [{ rootFiles: ["/source.ts"], compilerOptions: { noLib: true } }], + }); + assert.notEqual(cloned.id, source.id); + assert.equal((await cloned.operation.createdPrograms[0].getSourceFile("/source.ts"))?.text, "source"); + assert.throws(() => createFileSystemFromSnapshot(source, descriptor), /layer or full/); + for (const full of [false, true]) { + const extra = (full ? createFileSystem : createFileSystemLayer)([ + ["/extra.ts", "extra"], + ], { + directories: { "/listed": { files: ["entry.ts"], directories: [] } }, + symlinks: { "/alias.ts": { target: "/extra.ts" } }, + removedPaths: ["/removed"], + }); + const fileSystem = createFileSystemFromSnapshot(source, extra); + assert.strictEqual(fileSystem.fileSystem, extra); + await using snapshot = await api.createSnapshot({ + fileSystem, + createPrograms: [{ rootFiles: ["/extra.ts", "/alias.ts", "/source.ts", "/removed/nested.ts"], compilerOptions: { noLib: true } }], + }); + const program = snapshot.operation.createdPrograms[0]; + assert.equal((await program.getSourceFile("/extra.ts"))?.text, "extra"); + assert.equal((await program.getSourceFile("/alias.ts"))?.text, "extra"); + assert.equal((await program.getSourceFile("/source.ts"))?.text, full ? undefined : "source"); + assert.equal(await program.getSourceFile("/removed/nested.ts"), undefined); + } + }); + + for (const snapshotFileSystem of [false, true]) { + test(`Snapshot.update layers filesystem edits and removals with ${snapshotFileSystem ? "a snapshot" : "a layer"} filesystem`, async () => { + const update = (base: Snapshot, changes: CreateSnapshotParams) => + base.update( + snapshotFileSystem + ? { ...changes, fileSystem: createFileSystemFromSnapshot(base, changes.fileSystem) } + : changes, + ); + await using api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + }); + await using snapshot = await api.createSnapshot({ + openProjects: ["/tsconfig.json"], + fileSystem: createFileSystem(Object.entries({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { noLib: true }, + include: ["src/**/*.ts"], + }), + "/src/keep.ts": `export const keep = true;`, + "/src/change.ts": `export const version = "old";`, + "/src/remove.ts": `export const remove = true;`, + "/src/removed/gone.ts": `export const gone = true;`, + })), + }); + + await using updated = await update(snapshot, { + ensurePrograms: true, + fileSystem: createFileSystemLayer( + Object.entries({ + "/src/change.ts": `export const version = "new";`, + "/src/added.ts": `export const added = true;`, + }), + { + removedPaths: ["/src/remove.ts", "/src/removed"], + }, + ), + }); + const project = updated.getConfiguredProject("/tsconfig.json")!; + assert.equal((await project.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); + assert.equal((await project.program.getSourceFile("/src/change.ts"))?.text, `export const version = "new";`); + assert.equal((await project.program.getSourceFile("/src/added.ts"))?.text, `export const added = true;`); + assert.equal(await project.program.getSourceFile("/src/remove.ts"), undefined); + assert.equal(await project.program.getSourceFile("/src/removed/gone.ts"), undefined); + await using fork = await update(snapshot, { ensurePrograms: true }); + assert.equal((await fork.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/change.ts"))?.text, `export const version = "old";`); + + await using updatedAgain = await update(updated, { + ensurePrograms: true, + fileSystem: createFileSystemLayer( + Object.entries({ + "/src/added.ts": `export const added = "updated again";`, + }), + { + removedPaths: ["/src/change.ts"], + }, + ), + }); + const updatedAgainProject = updatedAgain.getConfiguredProject("/tsconfig.json")!; + assert.equal((await updatedAgainProject.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); + assert.equal((await updatedAgainProject.program.getSourceFile("/src/added.ts"))?.text, `export const added = "updated again";`); + assert.equal(await updatedAgainProject.program.getSourceFile("/src/change.ts"), undefined); }); + } - await using updated = await snapshot.update({ - ensurePrograms: true, - fileSystem: createFileSystemLayer( - Object.entries({ - "/src/change.ts": `export const version = "new";`, - "/src/added.ts": `export const added = true;`, - }), - { - removedPaths: ["/src/remove.ts", "/src/removed"], + test("Snapshot.update with a snapshot filesystem compares against the target's cached contents", async () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ "/index.ts": "export const value = 1;" }); + await using api = disposableAPI; + await using target = await api.createSnapshot({ + createPrograms: [{ rootFiles: ["/index.ts"], compilerOptions: { noLib: true } }], + }); + const programId = target.operation.createdPrograms[0].id; + const originalFile = await target.getProgram(programId)!.getSourceFile("/index.ts"); + assert.equal(originalFile!.text, "export const value = 1;"); + fs.writeFile!(toRootedFilePath("/index.ts", undefined), "export const value = 2;"); + await using source = await api.createSnapshot({ + fileSystem: createFileSystemLayer([["/index.ts", "export const value = 2;"]]), + }); + await using rebased = await target.update({ fileSystem: createFileSystemFromSnapshot(source), ensurePrograms: true }); + assert.equal((await rebased.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + assert.strictEqual(await target.getProgram(programId)!.getSourceFile("/index.ts"), originalFile); + }); + + for (const fullReplacement of [false, true]) { + test(`Snapshot.update with a snapshot filesystem removes cached files with ${fullReplacement ? "a full replacement" : "matching tombstones"}`, async () => { + await using api = spawnAPI({}); + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, files: ["index.ts", "removed/keep.ts"] }), + "/index.ts": "export {};", + }; + await using target = await api.createSnapshot({ + openProjects: ["/tsconfig.json"], + fileSystem: { + kind: "full", + files: { ...files, "/removed/keep.ts": "export const keep = true;" }, + directories: { "/": { files: ["index.ts", "tsconfig.json"], directories: [] } }, + removedPaths: ["/removed"], }, - ), + }); + const originalFile = await target.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"); + assert.ok(originalFile); + await using self = await target.update({ fileSystem: createFileSystemFromSnapshot(target), ensurePrograms: true }); + assert.strictEqual(await self.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), originalFile); + await using source = await api.createSnapshot({ + fileSystem: fullReplacement + ? createFileSystem(Object.entries(files)) + : createFileSystemLayer([], { removedPaths: ["/removed"] }), + }); + await using rebased = await target.update({ fileSystem: createFileSystemFromSnapshot(source), ensurePrograms: true }); + assert.equal(await rebased.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), undefined); + assert.strictEqual(await target.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), originalFile); }); - const project = updated.getConfiguredProject("/tsconfig.json")!; - assert.equal((await project.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); - assert.equal((await project.program.getSourceFile("/src/change.ts"))?.text, `export const version = "new";`); - assert.equal((await project.program.getSourceFile("/src/added.ts"))?.text, `export const added = true;`); - assert.equal(await project.program.getSourceFile("/src/remove.ts"), undefined); - assert.equal(await project.program.getSourceFile("/src/removed/gone.ts"), undefined); - await using fork = await snapshot.update({ ensurePrograms: true }); - assert.equal((await fork.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/change.ts"))?.text, `export const version = "old";`); + } - await using updatedAgain = await updated.update({ + test("Snapshot.update with a snapshot filesystem preserves explicit full invalidation", async () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ "/index.ts": "export const value = 1;" }); + await using api = disposableAPI; + await using original = await api.createSnapshot({ + createPrograms: [{ rootFiles: ["/index.ts"], compilerOptions: { noLib: true } }], + }); + const programId = original.operation.createdPrograms[0].id; + await using target = await original.update({}); + fs.writeFile!(toRootedFilePath("/index.ts", undefined), "export const value = 2;"); + const changes = { fileNotifications: { invalidateAll: true }, ensurePrograms: true } as const; + await using updated = await original.update(changes); + assert.equal((await updated.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + for (const base of [original, target]) { + await using rebased = await base.update({ ...changes, fileSystem: createFileSystemFromSnapshot(original) }); + assert.equal((await rebased.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + } + assert.equal((await original.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 1;"); + }); + + test("Snapshot.update with a snapshot filesystem refreshes dependency auto-imports", async () => { + const fileName = "/home/project/index.ts"; + const dependency = "/home/project/node_modules/my-pkg/index.d.ts"; + await using api = spawnAPI({ + "/home/project/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true } }), + [fileName]: "package", + "/home/project/package.json": JSON.stringify({ dependencies: { "my-pkg": "1.0.0" } }), + "/home/project/node_modules/my-pkg/package.json": JSON.stringify({ name: "my-pkg", version: "1.0.0", types: "index.d.ts" }), + [dependency]: "export declare const packageOld: number;", + }); + await using target = await api.createSnapshot({ + openProjects: ["/home/project/tsconfig.json"], + prepareAutoImports: fileName, + }); + const completionNames = async (snapshot: Snapshot) => (await snapshot.getConfiguredProject("/home/project/tsconfig.json")!.languageService.getCompletionsAtPosition(fileName, 7, { includeSymbol: true }))!.entries.map(entry => entry.name); + assert.ok((await completionNames(target)).includes("packageOld")); + await using source = await target.update({ + prepareAutoImports: fileName, + fileSystem: createFileSystemLayer([[dependency, "export declare const packageNew: number;"]]), + }); + assert.ok((await completionNames(source)).includes("packageNew")); + for (const base of [target, source]) { + await using rebased = await base.update({ fileSystem: createFileSystemFromSnapshot(source), prepareAutoImports: fileName }); + const names = await completionNames(rebased); + assert.ok(names.includes("packageNew")); + assert.ok(!names.includes("packageOld")); + } + }); + + test("Snapshot.update applies snapshot filesystems over the target snapshot", async () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, include: ["src/**/*.ts"] }), + "/src/index.ts": "export const value = 1;", + "/other/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, files: ["index.ts"] }), + "/other/index.ts": "export const other = true;", + }); + await using api = disposableAPI; + await using original = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + await using extras = await original.update({ ensurePrograms: true, - fileSystem: createFileSystemLayer( - Object.entries({ - "/src/added.ts": `export const added = "updated again";`, - }), - { - removedPaths: ["/src/change.ts"], - }, - ), + fileSystem: createFileSystemLayer([ + ["/src/extra.ts", "export const extra = true;"], + ], { removedPaths: ["/src/removed"] }), }); - const updatedAgainProject = updatedAgain.getConfiguredProject("/tsconfig.json")!; - assert.equal((await updatedAgainProject.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); - assert.equal((await updatedAgainProject.program.getSourceFile("/src/added.ts"))?.text, `export const added = "updated again";`); - assert.equal(await updatedAgainProject.program.getSourceFile("/src/change.ts"), undefined); + await using moreExtras = await extras.update({ + fileSystem: createFileSystemLayer([["/src/second.ts", "export const second = true;"]]), + }); + fs.writeFile!(toRootedFilePath("/src/index.ts", undefined), "export const value = 2;"); + await using newOriginal = await original.update({ + openProjects: ["/other/tsconfig.json"], + ensurePrograms: true, + fileNotifications: { changed: ["/src/index.ts"] }, + fileSystem: createFileSystemLayer([ + ["/src/extra.ts", "export const extra = false;"], + ["/src/target.ts", "export const target = true;"], + ["/src/removed/nested.ts", "export const removed = true;"], + ]), + }); + await using rebased = await newOriginal.update({ + prepareAutoImports: "/src/index.ts", + openFiles: ["/src/index.ts"], + createPrograms: [{ rootFiles: ["/src/extra.ts"], compilerOptions: { noLib: true } }], + fileSystem: createFileSystemFromSnapshot(moreExtras, createFileSystemLayer([["/src/second.ts", "export const second = false;"]])), + }); + assert.notEqual(rebased.id, moreExtras.id); + assert.notEqual(rebased.id, newOriginal.id); + assert.ok(rebased.getConfiguredProject("/other/tsconfig.json")); + assert.strictEqual(rebased.operation.openedFiles[0].project, rebased.getConfiguredProject("/tsconfig.json")); + assert.equal((await rebased.operation.createdPrograms[0].getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + const program = rebased.getConfiguredProject("/tsconfig.json")!.program; + assert.equal((await program.getSourceFile("/src/index.ts"))!.text, "export const value = 2;"); + assert.equal((await program.getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + assert.equal((await program.getSourceFile("/src/second.ts"))!.text, "export const second = false;"); + assert.equal((await program.getSourceFile("/src/target.ts"))!.text, "export const target = true;"); + assert.equal(await program.getSourceFile("/src/removed/nested.ts"), undefined); + assert.equal((await extras.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"))!.text, "export const value = 1;"); + assert.equal((await newOriginal.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/extra.ts"))!.text, "export const extra = false;"); + await extras.dispose(); + await moreExtras.dispose(); + await newOriginal.dispose(); + assert.equal((await program.getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + await using rebasedAgain = await original.update({ fileSystem: createFileSystemFromSnapshot(rebased), ensurePrograms: true }); + assert.equal((await rebasedAgain.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/target.ts"))!.text, "export const target = true;"); + assert.throws(() => createFileSystemFromSnapshot(moreExtras), /disposed/); + assert.throws(() => newOriginal.update({ fileSystem: createFileSystemFromSnapshot(rebased) }), /disposed/); + // @sync: assert.throws(() => original.update({ fileSystem: { kind: "snapshot", snapshot: moreExtras.id, files: {} } }), /inactive snapshot/); + await assert.rejects(original.update({ fileSystem: { kind: "snapshot", snapshot: moreExtras.id, files: {} } }), /inactive snapshot/); // @sync-skip }); test("eager snapshot disposal does not retain filesystem history", async () => { diff --git a/packages/typescript/test/sync/api-generators.test.ts b/packages/typescript/test/sync/api-generators.test.ts index 314052b63f789..bae80333e1ba3 100644 --- a/packages/typescript/test/sync/api-generators.test.ts +++ b/packages/typescript/test/sync/api-generators.test.ts @@ -22,6 +22,7 @@ import { SyntaxKind, } from "@typescript/typescript/ast"; import { cloneNode } from "@typescript/typescript/ast/factory"; +import { createFileSystemFromSnapshot } from "@typescript/typescript/fs"; import { toRootedFilePath } from "@typescript/typescript/path"; import type { APIRequest, @@ -1790,8 +1791,12 @@ describe("API - generator batching", { concurrency: areTestsFiltered() }, () => runParityBatch(api, cases); assert.deepEqual(temporaryProjects, ["/tsconfig.json", "/tsconfig.json"]); - const snapshotGeneratorAPI = spawnAPI(parityFiles); - const snapshotSyncAPI = spawnAPI(parityFiles); + const snapshotFiles = { + ...parityFiles, + "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, files: ["src/syntax.ts"] }), + }; + const snapshotGeneratorAPI = spawnAPI(snapshotFiles); + const snapshotSyncAPI = spawnAPI(snapshotFiles); try { const generatorBase = snapshotGeneratorAPI.batch(snapshotGeneratorAPI.createSnapshot.gen({ openProjects: ["/tsconfig.json"] }))[0]; const syncBase = snapshotSyncAPI.createSnapshot({ openProjects: ["/tsconfig.json"] }); @@ -1799,6 +1804,16 @@ describe("API - generator batching", { concurrency: areTestsFiltered() }, () => const syncUpdated = syncBase.update({}); assertSnapshotsEquivalent(generatorUpdated, syncUpdated, "Snapshot.update"); exercisedMethods.add("Snapshot.update"); + const changes = { + fileSystem: { kind: "layer", files: { "/src/syntax.ts": "export const fixed = true;" } }, + } as const; + const generatorExtras = snapshotGeneratorAPI.batch(generatorUpdated.update.gen(changes))[0]; + const syncExtras = syncUpdated.update(changes); + const snapshotChanges = { prepareAutoImports: "/src/syntax.ts", openFiles: ["/src/syntax.ts"] } as const; + const generatorOverlay = snapshotGeneratorAPI.batch(generatorBase.update.gen({ ...snapshotChanges, fileSystem: createFileSystemFromSnapshot(generatorExtras) }))[0]; + const syncOverlay = syncBase.update({ ...snapshotChanges, fileSystem: createFileSystemFromSnapshot(syncExtras) }); + assertSnapshotsEquivalent(generatorOverlay, syncOverlay, "Snapshot.update with a snapshot filesystem"); + assert.equal(generatorOverlay.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/syntax.ts")!.text, "export const fixed = true;"); } finally { snapshotGeneratorAPI.close(); diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index f048e8ede87e6..6d25e0d691258 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -70,6 +70,7 @@ import { import { visitEachChild } from "@typescript/typescript/ast/visitor"; import { createFileSystem, + createFileSystemFromSnapshot, createFileSystemLayer, createFileSystemWithLib, type FileSystemCallbacks, @@ -88,6 +89,7 @@ import { type CompilerOptions, type ConditionalType, type ConfiguredProjectId, + type CreateSnapshotParams, DiagnosticCategory, type DocumentIdentifier, EmitOnly, @@ -1900,6 +1902,51 @@ describe("BuildOrchestrator", () => { }); describe("Checker - getImmediateAliasedSymbol", { concurrency }, () => { + for (const declaration of [`import * as X from "./mod";`, `export * as X from "./mod";`]) { + for (const moduleContent of [`export default class User {}`, `export class User {}`]) { + test(`preserves module symbol identity for ${declaration} with ${moduleContent}`, () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/mod.ts": moduleContent, + "/src/main.ts": declaration, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const checker = snapshot.getConfiguredProject("/tsconfig.json")!.checker; + const moduleSymbol = checker.getSymbolOfSourceFile("/src/mod.ts"); + assert.ok(moduleSymbol); + const aliasSymbol = checker.getSymbolAtPosition("/src/main.ts", declaration.indexOf("X")); + assert.ok(aliasSymbol); + assert.equal(checker.getImmediateAliasedSymbol(aliasSymbol) === moduleSymbol, true); + assert.equal(checker.getImmediateAliasedSymbol(aliasSymbol) === moduleSymbol, true); + }); + } + } + + test("preserves the export assignment alias of a namespace import", () => { + const declaration = `import * as X from "./mod";`; + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/user.ts": `export class User {}`, + "/src/mod.ts": `import { User } from "./user"; export = User;`, + "/src/main.ts": declaration, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const checker = snapshot.getConfiguredProject("/tsconfig.json")!.checker; + const moduleSymbol = checker.getSymbolOfSourceFile("/src/mod.ts"); + assert.ok(moduleSymbol); + const exportAssignment = (moduleSymbol.getExports()).get(InternalSymbolName.ExportEquals); + assert.ok(exportAssignment); + const aliasSymbol = checker.getSymbolAtPosition("/src/main.ts", declaration.indexOf("X")); + assert.ok(aliasSymbol); + assert.equal(checker.getImmediateAliasedSymbol(aliasSymbol) === exportAssignment, true); + const resolved = checker.getAliasedSymbol(aliasSymbol); + assert.ok(resolved); + assert.equal(resolved.name, "User"); + assert.ok(resolved.flags & SymbolFlags.Class); + }); + test("resolves one level of alias indirection", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5351,60 +5398,265 @@ describe("updateSnapshot file systems", { concurrency }, () => { assert.deepEqual(callbackCalls, []); }); - test("Snapshot.update layers filesystem edits and removals", () => { - using api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + test("createFileSystemFromSnapshot supports creating snapshots with additional layer and full filesystems", () => { + using api = spawnAPI({}); + using source = api.createSnapshot({ + fileSystem: createFileSystemLayer([ + ["/source.ts", "source"], + ["/removed/nested.ts", "removed"], + ]), }); - using snapshot = api.createSnapshot({ - openProjects: ["/tsconfig.json"], - fileSystem: createFileSystem(Object.entries({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { noLib: true }, - include: ["src/**/*.ts"], - }), - "/src/keep.ts": `export const keep = true;`, - "/src/change.ts": `export const version = "old";`, - "/src/remove.ts": `export const remove = true;`, - "/src/removed/gone.ts": `export const gone = true;`, - })), + const descriptor = createFileSystemFromSnapshot(source); + assert.equal(descriptor.kind, "snapshot"); + assert.equal(descriptor.snapshot, source.id); + assert.deepEqual(descriptor.files, {}); + using cloned = api.createSnapshot({ + fileSystem: descriptor, + createPrograms: [{ rootFiles: ["/source.ts"], compilerOptions: { noLib: true } }], + }); + assert.notEqual(cloned.id, source.id); + assert.equal((cloned.operation.createdPrograms[0].getSourceFile("/source.ts"))?.text, "source"); + assert.throws(() => createFileSystemFromSnapshot(source, descriptor), /layer or full/); + for (const full of [false, true]) { + const extra = (full ? createFileSystem : createFileSystemLayer)([ + ["/extra.ts", "extra"], + ], { + directories: { "/listed": { files: ["entry.ts"], directories: [] } }, + symlinks: { "/alias.ts": { target: "/extra.ts" } }, + removedPaths: ["/removed"], + }); + const fileSystem = createFileSystemFromSnapshot(source, extra); + assert.strictEqual(fileSystem.fileSystem, extra); + using snapshot = api.createSnapshot({ + fileSystem, + createPrograms: [{ rootFiles: ["/extra.ts", "/alias.ts", "/source.ts", "/removed/nested.ts"], compilerOptions: { noLib: true } }], + }); + const program = snapshot.operation.createdPrograms[0]; + assert.equal((program.getSourceFile("/extra.ts"))?.text, "extra"); + assert.equal((program.getSourceFile("/alias.ts"))?.text, "extra"); + assert.equal((program.getSourceFile("/source.ts"))?.text, full ? undefined : "source"); + assert.equal(program.getSourceFile("/removed/nested.ts"), undefined); + } + }); + + for (const snapshotFileSystem of [false, true]) { + test(`Snapshot.update layers filesystem edits and removals with ${snapshotFileSystem ? "a snapshot" : "a layer"} filesystem`, () => { + const update = (base: Snapshot, changes: CreateSnapshotParams) => + base.update( + snapshotFileSystem + ? { ...changes, fileSystem: createFileSystemFromSnapshot(base, changes.fileSystem) } + : changes, + ); + using api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + }); + using snapshot = api.createSnapshot({ + openProjects: ["/tsconfig.json"], + fileSystem: createFileSystem(Object.entries({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { noLib: true }, + include: ["src/**/*.ts"], + }), + "/src/keep.ts": `export const keep = true;`, + "/src/change.ts": `export const version = "old";`, + "/src/remove.ts": `export const remove = true;`, + "/src/removed/gone.ts": `export const gone = true;`, + })), + }); + + using updated = update(snapshot, { + ensurePrograms: true, + fileSystem: createFileSystemLayer( + Object.entries({ + "/src/change.ts": `export const version = "new";`, + "/src/added.ts": `export const added = true;`, + }), + { + removedPaths: ["/src/remove.ts", "/src/removed"], + }, + ), + }); + const project = updated.getConfiguredProject("/tsconfig.json")!; + assert.equal((project.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); + assert.equal((project.program.getSourceFile("/src/change.ts"))?.text, `export const version = "new";`); + assert.equal((project.program.getSourceFile("/src/added.ts"))?.text, `export const added = true;`); + assert.equal(project.program.getSourceFile("/src/remove.ts"), undefined); + assert.equal(project.program.getSourceFile("/src/removed/gone.ts"), undefined); + using fork = update(snapshot, { ensurePrograms: true }); + assert.equal((fork.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/change.ts"))?.text, `export const version = "old";`); + + using updatedAgain = update(updated, { + ensurePrograms: true, + fileSystem: createFileSystemLayer( + Object.entries({ + "/src/added.ts": `export const added = "updated again";`, + }), + { + removedPaths: ["/src/change.ts"], + }, + ), + }); + const updatedAgainProject = updatedAgain.getConfiguredProject("/tsconfig.json")!; + assert.equal((updatedAgainProject.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); + assert.equal((updatedAgainProject.program.getSourceFile("/src/added.ts"))?.text, `export const added = "updated again";`); + assert.equal(updatedAgainProject.program.getSourceFile("/src/change.ts"), undefined); }); + } - using updated = snapshot.update({ - ensurePrograms: true, - fileSystem: createFileSystemLayer( - Object.entries({ - "/src/change.ts": `export const version = "new";`, - "/src/added.ts": `export const added = true;`, - }), - { - removedPaths: ["/src/remove.ts", "/src/removed"], + test("Snapshot.update with a snapshot filesystem compares against the target's cached contents", () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ "/index.ts": "export const value = 1;" }); + using api = disposableAPI; + using target = api.createSnapshot({ + createPrograms: [{ rootFiles: ["/index.ts"], compilerOptions: { noLib: true } }], + }); + const programId = target.operation.createdPrograms[0].id; + const originalFile = target.getProgram(programId)!.getSourceFile("/index.ts"); + assert.equal(originalFile!.text, "export const value = 1;"); + fs.writeFile!(toRootedFilePath("/index.ts", undefined), "export const value = 2;"); + using source = api.createSnapshot({ + fileSystem: createFileSystemLayer([["/index.ts", "export const value = 2;"]]), + }); + using rebased = target.update({ fileSystem: createFileSystemFromSnapshot(source), ensurePrograms: true }); + assert.equal((rebased.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + assert.strictEqual(target.getProgram(programId)!.getSourceFile("/index.ts"), originalFile); + }); + + for (const fullReplacement of [false, true]) { + test(`Snapshot.update with a snapshot filesystem removes cached files with ${fullReplacement ? "a full replacement" : "matching tombstones"}`, () => { + using api = spawnAPI({}); + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, files: ["index.ts", "removed/keep.ts"] }), + "/index.ts": "export {};", + }; + using target = api.createSnapshot({ + openProjects: ["/tsconfig.json"], + fileSystem: { + kind: "full", + files: { ...files, "/removed/keep.ts": "export const keep = true;" }, + directories: { "/": { files: ["index.ts", "tsconfig.json"], directories: [] } }, + removedPaths: ["/removed"], }, - ), + }); + const originalFile = target.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"); + assert.ok(originalFile); + using self = target.update({ fileSystem: createFileSystemFromSnapshot(target), ensurePrograms: true }); + assert.strictEqual(self.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), originalFile); + using source = api.createSnapshot({ + fileSystem: fullReplacement + ? createFileSystem(Object.entries(files)) + : createFileSystemLayer([], { removedPaths: ["/removed"] }), + }); + using rebased = target.update({ fileSystem: createFileSystemFromSnapshot(source), ensurePrograms: true }); + assert.equal(rebased.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), undefined); + assert.strictEqual(target.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/removed/keep.ts"), originalFile); }); - const project = updated.getConfiguredProject("/tsconfig.json")!; - assert.equal((project.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); - assert.equal((project.program.getSourceFile("/src/change.ts"))?.text, `export const version = "new";`); - assert.equal((project.program.getSourceFile("/src/added.ts"))?.text, `export const added = true;`); - assert.equal(project.program.getSourceFile("/src/remove.ts"), undefined); - assert.equal(project.program.getSourceFile("/src/removed/gone.ts"), undefined); - using fork = snapshot.update({ ensurePrograms: true }); - assert.equal((fork.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/change.ts"))?.text, `export const version = "old";`); + } - using updatedAgain = updated.update({ + test("Snapshot.update with a snapshot filesystem preserves explicit full invalidation", () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ "/index.ts": "export const value = 1;" }); + using api = disposableAPI; + using original = api.createSnapshot({ + createPrograms: [{ rootFiles: ["/index.ts"], compilerOptions: { noLib: true } }], + }); + const programId = original.operation.createdPrograms[0].id; + using target = original.update({}); + fs.writeFile!(toRootedFilePath("/index.ts", undefined), "export const value = 2;"); + const changes = { fileNotifications: { invalidateAll: true }, ensurePrograms: true } as const; + using updated = original.update(changes); + assert.equal((updated.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + for (const base of [original, target]) { + using rebased = base.update({ ...changes, fileSystem: createFileSystemFromSnapshot(original) }); + assert.equal((rebased.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 2;"); + } + assert.equal((original.getProgram(programId)!.getSourceFile("/index.ts"))!.text, "export const value = 1;"); + }); + + test("Snapshot.update with a snapshot filesystem refreshes dependency auto-imports", () => { + const fileName = "/home/project/index.ts"; + const dependency = "/home/project/node_modules/my-pkg/index.d.ts"; + using api = spawnAPI({ + "/home/project/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true } }), + [fileName]: "package", + "/home/project/package.json": JSON.stringify({ dependencies: { "my-pkg": "1.0.0" } }), + "/home/project/node_modules/my-pkg/package.json": JSON.stringify({ name: "my-pkg", version: "1.0.0", types: "index.d.ts" }), + [dependency]: "export declare const packageOld: number;", + }); + using target = api.createSnapshot({ + openProjects: ["/home/project/tsconfig.json"], + prepareAutoImports: fileName, + }); + const completionNames = (snapshot: Snapshot) => (snapshot.getConfiguredProject("/home/project/tsconfig.json")!.languageService.getCompletionsAtPosition(fileName, 7, { includeSymbol: true }))!.entries.map(entry => entry.name); + assert.ok((completionNames(target)).includes("packageOld")); + using source = target.update({ + prepareAutoImports: fileName, + fileSystem: createFileSystemLayer([[dependency, "export declare const packageNew: number;"]]), + }); + assert.ok((completionNames(source)).includes("packageNew")); + for (const base of [target, source]) { + using rebased = base.update({ fileSystem: createFileSystemFromSnapshot(source), prepareAutoImports: fileName }); + const names = completionNames(rebased); + assert.ok(names.includes("packageNew")); + assert.ok(!names.includes("packageOld")); + } + }); + + test("Snapshot.update applies snapshot filesystems over the target snapshot", () => { + const { api: disposableAPI, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, include: ["src/**/*.ts"] }), + "/src/index.ts": "export const value = 1;", + "/other/tsconfig.json": JSON.stringify({ compilerOptions: { noLib: true }, files: ["index.ts"] }), + "/other/index.ts": "export const other = true;", + }); + using api = disposableAPI; + using original = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + using extras = original.update({ ensurePrograms: true, - fileSystem: createFileSystemLayer( - Object.entries({ - "/src/added.ts": `export const added = "updated again";`, - }), - { - removedPaths: ["/src/change.ts"], - }, - ), + fileSystem: createFileSystemLayer([ + ["/src/extra.ts", "export const extra = true;"], + ], { removedPaths: ["/src/removed"] }), }); - const updatedAgainProject = updatedAgain.getConfiguredProject("/tsconfig.json")!; - assert.equal((updatedAgainProject.program.getSourceFile("/src/keep.ts"))?.text, `export const keep = true;`); - assert.equal((updatedAgainProject.program.getSourceFile("/src/added.ts"))?.text, `export const added = "updated again";`); - assert.equal(updatedAgainProject.program.getSourceFile("/src/change.ts"), undefined); + using moreExtras = extras.update({ + fileSystem: createFileSystemLayer([["/src/second.ts", "export const second = true;"]]), + }); + fs.writeFile!(toRootedFilePath("/src/index.ts", undefined), "export const value = 2;"); + using newOriginal = original.update({ + openProjects: ["/other/tsconfig.json"], + ensurePrograms: true, + fileNotifications: { changed: ["/src/index.ts"] }, + fileSystem: createFileSystemLayer([ + ["/src/extra.ts", "export const extra = false;"], + ["/src/target.ts", "export const target = true;"], + ["/src/removed/nested.ts", "export const removed = true;"], + ]), + }); + using rebased = newOriginal.update({ + prepareAutoImports: "/src/index.ts", + openFiles: ["/src/index.ts"], + createPrograms: [{ rootFiles: ["/src/extra.ts"], compilerOptions: { noLib: true } }], + fileSystem: createFileSystemFromSnapshot(moreExtras, createFileSystemLayer([["/src/second.ts", "export const second = false;"]])), + }); + assert.notEqual(rebased.id, moreExtras.id); + assert.notEqual(rebased.id, newOriginal.id); + assert.ok(rebased.getConfiguredProject("/other/tsconfig.json")); + assert.strictEqual(rebased.operation.openedFiles[0].project, rebased.getConfiguredProject("/tsconfig.json")); + assert.equal((rebased.operation.createdPrograms[0].getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + const program = rebased.getConfiguredProject("/tsconfig.json")!.program; + assert.equal((program.getSourceFile("/src/index.ts"))!.text, "export const value = 2;"); + assert.equal((program.getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + assert.equal((program.getSourceFile("/src/second.ts"))!.text, "export const second = false;"); + assert.equal((program.getSourceFile("/src/target.ts"))!.text, "export const target = true;"); + assert.equal(program.getSourceFile("/src/removed/nested.ts"), undefined); + assert.equal((extras.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"))!.text, "export const value = 1;"); + assert.equal((newOriginal.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/extra.ts"))!.text, "export const extra = false;"); + extras.dispose(); + moreExtras.dispose(); + newOriginal.dispose(); + assert.equal((program.getSourceFile("/src/extra.ts"))!.text, "export const extra = true;"); + using rebasedAgain = original.update({ fileSystem: createFileSystemFromSnapshot(rebased), ensurePrograms: true }); + assert.equal((rebasedAgain.getConfiguredProject("/tsconfig.json")!.program.getSourceFile("/src/target.ts"))!.text, "export const target = true;"); + assert.throws(() => createFileSystemFromSnapshot(moreExtras), /disposed/); + assert.throws(() => newOriginal.update({ fileSystem: createFileSystemFromSnapshot(rebased) }), /disposed/); + assert.throws(() => original.update({ fileSystem: { kind: "snapshot", snapshot: moreExtras.id, files: {} } }), /inactive snapshot/); }); test("eager snapshot disposal does not retain filesystem history", () => { diff --git a/tools/customlint/plugin.go b/tools/customlint/plugin.go index fd9c5b9e7dfce..677f1a0d9012a 100644 --- a/tools/customlint/plugin.go +++ b/tools/customlint/plugin.go @@ -22,6 +22,7 @@ func (f *plugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { forbidParentAccessAnalyzer, shadowAnalyzer, typedPathsAnalyzer, + unsafeLayoutAnalyzer, unexportedAPIAnalyzer, }, nil } diff --git a/tools/customlint/testdata/unsafelayout/consumer/consumer.go b/tools/customlint/testdata/unsafelayout/consumer/consumer.go new file mode 100644 index 0000000000000..e7117fb7492a6 --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/consumer/consumer.go @@ -0,0 +1,110 @@ +package consumer + +import ( + "unsafe" + + "testdata/unsafelayout/internal/ast" + "testdata/unsafelayout/internal/checker" +) + +type BadNode ast.Node + +type NodeAlias = ast.Node + +type BorrowedNode struct { + *ast.Node +} + +func uses(node *ast.Node, declaration *ast.ImportDeclaration, kind ast.Kind, typ *checker.Type) { + node.Kind = ast.KindIdentifier + declaration.Kind = ast.KindImportDeclaration + declaration.Kind = ast.KindJSImportDeclaration + node.AsImportDeclaration().Kind = ast.KindImportDeclaration + declaration.Kind = ast.KindIdentifier + declaration.Kind = kind + declaration.Kind++ + _ = &node.Kind + _ = &ast.Node{Kind: ast.KindIdentifier} + _ = &ast.Identifier{} + _ = new(ast.Node) + _ = new(ast.Identifier) + _ = new(NodeAlias) + _ = unsafe.Sizeof(ast.Node{}) + _ = unsafe.Offsetof(ast.Identifier{}.Node) + _ = (*ast.Identifier)(unsafe.Pointer(node)) + _ = *node + _ = *declaration + _ = *typ + copy := *node + _ = copy + pointer := unsafe.Pointer(node) + _ = pointer + borrowed := BorrowedNode{Node: node} + borrowed.Kind = ast.KindIdentifier + _ = &borrowed.Kind +} + +type Pointer unsafe.Pointer + +type PointerAlias = unsafe.Pointer + +func zeroValues(node *ast.Node) { + var identifier ast.Identifier + _ = &identifier + var identifiers [2]ast.Identifier + _ = &identifiers[0] + var holder struct{ identifier ast.Identifier } + _ = &holder.identifier + var pointers [2]*ast.Identifier + _ = pointers + _ = new([2]ast.Identifier) + _ = make([]ast.Identifier, 1) + _ = make([]*ast.Identifier, 1) + _ = [2]ast.Identifier{} + _ = (*ast.Identifier)(Pointer(node)) + _ = (*ast.Identifier)(PointerAlias(node)) +} + +func retag(node *ast.Node, kinds []ast.Kind) { + for _, node.Kind = range kinds { + } +} + +func zeroResult() (n ast.Identifier) { + return +} + +func byValueParam(identifier ast.Identifier, identifiers [2]ast.Identifier) {} + +func copies(records map[string]ast.Identifier, channel chan ast.Identifier, get func() ast.Identifier, slice []ast.Identifier) *ast.Identifier { + record := records["key"] + _ = <-channel + _ = get() + _ = slice[0] + _ = &slice[0] + _ = slice[0].Text + _ = len(slice[:1]) + return &record +} + +func rangeCopies(records []ast.Identifier, byKey map[string]ast.Identifier, channel chan ast.Identifier) { + for _, record := range records { + _ = record.Text + } + for _, record := range byKey { + _ = record.Text + } + for record := range channel { + _ = record.Text + } + for i := range records { + _ = &records[i] + } +} + +func bulkCopies(dst, src []ast.Identifier, pointers []*ast.Identifier) []ast.Identifier { + copy(dst, src) + pointers = append(pointers, pointers...) + copy(pointers, pointers) + return append(dst, src...) +} diff --git a/tools/customlint/testdata/unsafelayout/consumer/consumer.go.golden b/tools/customlint/testdata/unsafelayout/consumer/consumer.go.golden new file mode 100644 index 0000000000000..b09ccabe852b6 --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/consumer/consumer.go.golden @@ -0,0 +1,217 @@ + package consumer + + import ( + "unsafe" + + "testdata/unsafelayout/internal/ast" + "testdata/unsafelayout/internal/checker" + ) + + type BadNode ast.Node + ~ +!!! unsafelayout: defining a new compiler-record type bypasses checked construction; use a type alias + + type NodeAlias = ast.Node + + type BorrowedNode struct { + *ast.Node + } + + func uses(node *ast.Node, declaration *ast.ImportDeclaration, kind ast.Kind, typ *checker.Type) { + node.Kind = ast.KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = ast.KindImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = ast.KindJSImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + node.AsImportDeclaration().Kind = ast.KindImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = ast.KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = kind + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind++ + ~ +!!! unsafelayout: incrementing a compiler tag bypasses checked tag assignment + _ = &node.Kind + ~ +!!! unsafelayout: taking a compiler tag's address bypasses checked tag assignment + _ = &ast.Node{Kind: ast.KindIdentifier} + ~ +!!! unsafelayout: a bare compiler header has no concrete payload; use a concrete factory + ~ +!!! unsafelayout: construct compiler records through their owning factories, not struct literals + ~ +!!! unsafelayout: compiler tags must be assigned by checked construction, not struct literals + _ = &ast.Identifier{} + ~ +!!! unsafelayout: construct compiler records through their owning factories, not struct literals + _ = new(ast.Node) + ~ +!!! unsafelayout: a bare compiler header has no concrete payload; use a concrete factory + ~ +!!! unsafelayout: allocate compiler records through their owning factories + _ = new(ast.Identifier) + ~ +!!! unsafelayout: allocate compiler records through their owning factories + _ = new(NodeAlias) + ~ +!!! unsafelayout: a bare compiler header has no concrete payload; use a concrete factory + ~ +!!! unsafelayout: allocate compiler records through their owning factories + _ = unsafe.Sizeof(ast.Node{}) + _ = unsafe.Offsetof(ast.Identifier{}.Node) + _ = (*ast.Identifier)(unsafe.Pointer(node)) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + _ = *node + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = *declaration + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = *typ + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + copy := *node + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = copy + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + pointer := unsafe.Pointer(node) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + _ = pointer + borrowed := BorrowedNode{Node: node} + borrowed.Kind = ast.KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + _ = &borrowed.Kind + ~ +!!! unsafelayout: taking a compiler tag's address bypasses checked tag assignment + } + + type Pointer unsafe.Pointer + + type PointerAlias = unsafe.Pointer + + func zeroValues(node *ast.Node) { + var identifier ast.Identifier + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = &identifier + var identifiers [2]ast.Identifier + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = &identifiers[0] + var holder struct{ identifier ast.Identifier } + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = &holder.identifier + var pointers [2]*ast.Identifier + _ = pointers + _ = new([2]ast.Identifier) + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = make([]ast.Identifier, 1) + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = make([]*ast.Identifier, 1) + _ = [2]ast.Identifier{} + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = (*ast.Identifier)(Pointer(node)) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + _ = (*ast.Identifier)(PointerAlias(node)) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + } + + func retag(node *ast.Node, kinds []ast.Kind) { + for _, node.Kind = range kinds { + ~ +!!! unsafelayout: range assignment to a compiler tag bypasses checked tag assignment + } + } + + func zeroResult() (n ast.Identifier) { + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + return + } + + func byValueParam(identifier ast.Identifier, identifiers [2]ast.Identifier) {} + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + + func copies(records map[string]ast.Identifier, channel chan ast.Identifier, get func() ast.Identifier, slice []ast.Identifier) *ast.Identifier { + record := records["key"] + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = <-channel + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = get() + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = slice[0] + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + _ = &slice[0] + _ = slice[0].Text + _ = len(slice[:1]) + return &record + } + + func rangeCopies(records []ast.Identifier, byKey map[string]ast.Identifier, channel chan ast.Identifier) { + for _, record := range records { + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + _ = record.Text + } + for _, record := range byKey { + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + _ = record.Text + } + for record := range channel { + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + _ = record.Text + } + for i := range records { + _ = &records[i] + } + } + + func bulkCopies(dst, src []ast.Identifier, pointers []*ast.Identifier) []ast.Identifier { + copy(dst, src) + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + pointers = append(pointers, pointers...) + copy(pointers, pointers) + return append(dst, src...) + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + } + diff --git a/tools/customlint/testdata/unsafelayout/internal/ast/ast.go b/tools/customlint/testdata/unsafelayout/internal/ast/ast.go new file mode 100644 index 0000000000000..d0ffd3677cc2a --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/internal/ast/ast.go @@ -0,0 +1,103 @@ +package ast + +type Kind int16 + +const ( + KindIdentifier Kind = iota + KindImportDeclaration + KindJSImportDeclaration +) + +type Node struct { + Kind Kind +} + +type Identifier struct { + Node + Text string +} + +type ImportDeclaration struct { + Node +} + +func (n *Node) AsNode() *Node { return n } + +func (n *Node) AsIdentifier() *Identifier { + if n.Kind != KindIdentifier { + panic("Invalid node cast") + } + return nil +} + +func (n *Node) AsImportDeclaration() *ImportDeclaration { + switch n.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return nil + default: + panic("Invalid node cast") + } +} + +func newNode(kind Kind, node *Node) *Node { + node.Kind = kind + return node +} + +func NewIdentifier(text string) *Node { + data := &Identifier{Text: text} + return newNode(KindIdentifier, data.AsNode()) +} + +func badFactory() *Node { + data := &ImportDeclaration{} + return newNode(KindIdentifier, data.AsNode()) +} + +func escapes(kind Kind) *Node { + data := &Identifier{} + _ = newNode + return newNode(kind, data.AsNode()) +} + +type PretendIdentifier struct { + Identifier +} + +func (n *PretendIdentifier) AsNode() *Node { return &n.Node } + +func shadowedUpcast() *Node { + data := &PretendIdentifier{} + return newNode(KindIdentifier, data.AsNode()) +} + +func illegalWrites(node *Node, identifier *Identifier, declaration *ImportDeclaration, kind Kind) { + node.Kind = KindIdentifier + identifier.Kind = KindIdentifier + identifier.Kind = KindImportDeclaration + declaration.Kind = KindImportDeclaration + declaration.Kind = KindJSImportDeclaration + declaration.Kind = kind + declaration.Kind += KindImportDeclaration + node.Kind++ + _ = &node.Kind + (node.Kind) = KindIdentifier + (node.Kind)++ + _ = &(node.Kind) +} + +func freshNodes() *Node { + tagged := &Identifier{} + tagged.Text = "x" + _ = newNode(KindIdentifier, tagged.AsNode()) + return (&Identifier{}).AsNode() +} + +func zeroValueNode() *Identifier { + var n Identifier + return &n +} + +func (n Identifier) ValueReceiver() string { + return n.Text +} diff --git a/tools/customlint/testdata/unsafelayout/internal/ast/ast.go.golden b/tools/customlint/testdata/unsafelayout/internal/ast/ast.go.golden new file mode 100644 index 0000000000000..49b61d58be57d --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/internal/ast/ast.go.golden @@ -0,0 +1,142 @@ + package ast + + type Kind int16 + + const ( + KindIdentifier Kind = iota + KindImportDeclaration + KindJSImportDeclaration + ) + + type Node struct { + Kind Kind + } + + type Identifier struct { + Node + Text string + } + + type ImportDeclaration struct { + Node + } + + func (n *Node) AsNode() *Node { return n } + + func (n *Node) AsIdentifier() *Identifier { + if n.Kind != KindIdentifier { + panic("Invalid node cast") + } + return nil + } + + func (n *Node) AsImportDeclaration() *ImportDeclaration { + switch n.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return nil + default: + panic("Invalid node cast") + } + } + + func newNode(kind Kind, node *Node) *Node { + node.Kind = kind + return node + } + + func NewIdentifier(text string) *Node { + data := &Identifier{Text: text} + return newNode(KindIdentifier, data.AsNode()) + } + + func badFactory() *Node { + data := &ImportDeclaration{} + return newNode(KindIdentifier, data.AsNode()) + ~ +!!! unsafelayout: newNode must pair a concrete AsNode() receiver with a compatible kind + } + + func escapes(kind Kind) *Node { + data := &Identifier{} + _ = newNode + ~ +!!! unsafelayout: checked construction must be called directly so its allocation/tag pairing can be verified + return newNode(kind, data.AsNode()) + ~ +!!! unsafelayout: newNode must pair a concrete AsNode() receiver with a compatible kind + } + + type PretendIdentifier struct { + Identifier + } + + func (n *PretendIdentifier) AsNode() *Node { return &n.Node } + + func shadowedUpcast() *Node { + data := &PretendIdentifier{} + return newNode(KindIdentifier, data.AsNode()) + ~ +!!! unsafelayout: newNode must pair a concrete AsNode() receiver with a compatible kind + } + + func illegalWrites(node *Node, identifier *Identifier, declaration *ImportDeclaration, kind Kind) { + node.Kind = KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + identifier.Kind = KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + identifier.Kind = KindImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = KindImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = KindJSImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind = kind + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + declaration.Kind += KindImportDeclaration + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + node.Kind++ + ~ +!!! unsafelayout: incrementing a compiler tag bypasses checked tag assignment + _ = &node.Kind + ~ +!!! unsafelayout: taking a compiler tag's address bypasses checked tag assignment + (node.Kind) = KindIdentifier + ~ +!!! unsafelayout: AST kind is immutable outside checked construction; create a new node + (node.Kind)++ + ~ +!!! unsafelayout: incrementing a compiler tag bypasses checked tag assignment + _ = &(node.Kind) + ~ +!!! unsafelayout: taking a compiler tag's address bypasses checked tag assignment + } + + func freshNodes() *Node { + tagged := &Identifier{} + tagged.Text = "x" + _ = newNode(KindIdentifier, tagged.AsNode()) + return (&Identifier{}).AsNode() + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + } + + func zeroValueNode() *Identifier { + var n Identifier + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + return &n + } + + func (n Identifier) ValueReceiver() string { + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + return n.Text + } + diff --git a/tools/customlint/testdata/unsafelayout/internal/checker/checker.go b/tools/customlint/testdata/unsafelayout/internal/checker/checker.go new file mode 100644 index 0000000000000..220982c78358d --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/internal/checker/checker.go @@ -0,0 +1,176 @@ +package checker + +import "unsafe" + +type typeKind uint8 + +const ( + typeKindLiteral typeKind = iota + typeKindObject +) + +type Type struct { + kind typeKind +} + +type LiteralType struct { + TypeBase +} + +type ObjectType struct { + TypeBase +} + +type TypeBase struct { + Type +} + +func (t *TypeBase) AsType() *Type { return &t.Type } + +type Checker struct{} + +func (c *Checker) newType(flags, objectFlags int, typ *Type, kind typeKind) *Type { + typ.kind = kind + if flags != 0 { + kind := typeKindObject + typ.kind = kind + } + return typ +} + +func castType[T any](t *Type, kind typeKind) *T { + if t.kind != kind { + panic("Invalid type cast") + } + return (*T)(unsafe.Pointer(t)) +} + +func calls(c *Checker, literal *LiteralType, typ *Type, kind typeKind) { + c.newType(0, 0, literal.AsType(), typeKindLiteral) + c.newType(0, 0, literal.AsType(), typeKindObject) + c.newType(0, 0, literal.AsType(), kind) + c.newType(0, 0, typ, typeKindLiteral) + _ = c.newType + _ = castType[LiteralType](typ, typeKindLiteral) + _ = castType[LiteralType](typ, typeKindObject) + _ = castType[ObjectType](typ, typeKindLiteral) + _ = castType[LiteralType](typ, kind) + _ = castType[*LiteralType](typ, typeKindLiteral) + _ = castType[LiteralType] + typ.kind = typeKindLiteral + typ.kind++ + _ = &typ.kind + _ = (*LiteralType)(unsafe.Pointer(typ)) +} + +type PretendLiteralType struct { + LiteralType +} + +func (t *PretendLiteralType) AsType() *Type { return &t.Type } + +func shadowedUpcast(c *Checker) { + data := &PretendLiteralType{} + c.newType(0, 0, data.AsType(), typeKindLiteral) +} + +type TypeMapperKind uint8 + +const ( + TypeMapperKindSimple TypeMapperKind = iota + TypeMapperKindArray +) + +type TypeMapper struct { + kind TypeMapperKind +} + +type SimpleTypeMapper struct { + TypeMapper +} + +type ArrayTypeMapper struct { + TypeMapper +} + +func castTypeMapper[T any](m *TypeMapper, kind TypeMapperKind) *T { + if m.kind != kind { + panic("Invalid type mapper cast") + } + return (*T)(unsafe.Pointer(m)) +} + +func mapperCalls(simple *SimpleTypeMapper, header *TypeMapper) { + simple.kind = TypeMapperKindSimple + simple.kind = TypeMapperKindArray + header.kind = TypeMapperKindSimple + _ = castTypeMapper[SimpleTypeMapper](header, TypeMapperKindSimple) + _ = castTypeMapper[SimpleTypeMapper](header, TypeMapperKindArray) + _ = (*SimpleTypeMapper)(unsafe.Pointer(header)) +} + +func freshRecords(c *Checker) *Type { + tagged := &LiteralType{} + c.newType(0, 0, tagged.AsType(), typeKindLiteral) + _ = (&LiteralType{}).AsType() + escaped := &LiteralType{} + c.newType(0, 0, escaped.AsType(), typeKindLiteral) + _ = escaped.AsType() + header := &ObjectType{} + _ = &header.TypeBase + c.newType(0, 0, header.AsType(), typeKindObject) + unused := &ObjectType{} + _ = unused + value := LiteralType{} + _ = value + return new(LiteralType).AsType() +} + +func freshMappers() (*TypeMapper, *TypeMapper) { + tagged := &SimpleTypeMapper{} + tagged.kind = TypeMapperKindSimple + untagged := &ArrayTypeMapper{} + return &tagged.TypeMapper, &untagged.TypeMapper +} + +type arena[T any] struct{} + +func (a *arena[T]) New() *T { return new(T) } + +func arenaRecords(c *Checker, literals *arena[LiteralType]) *LiteralType { + c.newType(0, 0, literals.New().AsType(), typeKindLiteral) + data := literals.New() + c.newType(0, 0, data.AsType(), typeKindLiteral) + return literals.New() +} + +func zeroValueRecords() (*LiteralType, *SimpleTypeMapper) { + var literal LiteralType + var literals [2]LiteralType + _ = literals + var mappers [2]SimpleTypeMapper + mappers[0].kind = TypeMapperKindSimple + return &literal, &mappers[0] +} + +func (m *SimpleTypeMapper) Map(t *Type) *Type { return t } + +func initSimpleTypeMapper(m *SimpleTypeMapper) *TypeMapper { + m.kind = TypeMapperKindSimple + return &m.TypeMapper +} + +type mapperOwner struct { + mapperStorage [2]SimpleTypeMapper +} + +func coallocatedMappers(owner *mapperOwner, t *Type) (*TypeMapper, *TypeMapper, *Type) { + tagged := initSimpleTypeMapper(&owner.mapperStorage[0]) + escaped := &owner.mapperStorage[1].TypeMapper + return tagged, escaped, owner.mapperStorage[1].Map(t) +} + +func zeroValueMapper() *TypeMapper { + var m SimpleTypeMapper + return &m.TypeMapper +} diff --git a/tools/customlint/testdata/unsafelayout/internal/checker/checker.go.golden b/tools/customlint/testdata/unsafelayout/internal/checker/checker.go.golden new file mode 100644 index 0000000000000..423cada86003d --- /dev/null +++ b/tools/customlint/testdata/unsafelayout/internal/checker/checker.go.golden @@ -0,0 +1,257 @@ + package checker + + import "unsafe" + + type typeKind uint8 + + const ( + typeKindLiteral typeKind = iota + typeKindObject + ) + + type Type struct { + kind typeKind + } + + type LiteralType struct { + TypeBase + } + + type ObjectType struct { + TypeBase + } + + type TypeBase struct { + Type + } + + func (t *TypeBase) AsType() *Type { return &t.Type } + + type Checker struct{} + + func (c *Checker) newType(flags, objectFlags int, typ *Type, kind typeKind) *Type { + typ.kind = kind + if flags != 0 { + kind := typeKindObject + typ.kind = kind + ~ +!!! unsafelayout: checked construction must initialize the tag from its kind parameter + } + return typ + } + + func castType[T any](t *Type, kind typeKind) *T { + if t.kind != kind { + panic("Invalid type cast") + } + return (*T)(unsafe.Pointer(t)) + } + + func calls(c *Checker, literal *LiteralType, typ *Type, kind typeKind) { + c.newType(0, 0, literal.AsType(), typeKindLiteral) + c.newType(0, 0, literal.AsType(), typeKindObject) + ~ +!!! unsafelayout: newType must directly pair a concrete AsType() receiver with its matching constant kind + c.newType(0, 0, literal.AsType(), kind) + ~ +!!! unsafelayout: newType must directly pair a concrete AsType() receiver with its matching constant kind + c.newType(0, 0, typ, typeKindLiteral) + ~ +!!! unsafelayout: newType must directly pair a concrete AsType() receiver with its matching constant kind + _ = c.newType + ~ +!!! unsafelayout: checked construction must be called directly so its allocation/tag pairing can be verified + _ = castType[LiteralType](typ, typeKindLiteral) + _ = castType[LiteralType](typ, typeKindObject) + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + _ = castType[ObjectType](typ, typeKindLiteral) + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + _ = castType[LiteralType](typ, kind) + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + _ = castType[*LiteralType](typ, typeKindLiteral) + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + _ = castType[LiteralType] + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + typ.kind = typeKindLiteral + ~ +!!! unsafelayout: type kind is immutable outside checked construction + typ.kind++ + ~ +!!! unsafelayout: incrementing a compiler tag bypasses checked tag assignment + _ = &typ.kind + ~ +!!! unsafelayout: taking a compiler tag's address bypasses checked tag assignment + _ = (*LiteralType)(unsafe.Pointer(typ)) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + } + + type PretendLiteralType struct { + LiteralType + } + + func (t *PretendLiteralType) AsType() *Type { return &t.Type } + + func shadowedUpcast(c *Checker) { + data := &PretendLiteralType{} + c.newType(0, 0, data.AsType(), typeKindLiteral) + ~ +!!! unsafelayout: newType must directly pair a concrete AsType() receiver with its matching constant kind + } + + type TypeMapperKind uint8 + + const ( + TypeMapperKindSimple TypeMapperKind = iota + TypeMapperKindArray + ) + + type TypeMapper struct { + kind TypeMapperKind + } + + type SimpleTypeMapper struct { + TypeMapper + } + + type ArrayTypeMapper struct { + TypeMapper + } + + func castTypeMapper[T any](m *TypeMapper, kind TypeMapperKind) *T { + if m.kind != kind { + panic("Invalid type mapper cast") + } + return (*T)(unsafe.Pointer(m)) + } + + func mapperCalls(simple *SimpleTypeMapper, header *TypeMapper) { + simple.kind = TypeMapperKindSimple + simple.kind = TypeMapperKindArray + ~ +!!! unsafelayout: mapper tag must match the concrete mapper allocation + header.kind = TypeMapperKindSimple + ~ +!!! unsafelayout: mapper tag must match the concrete mapper allocation + _ = castTypeMapper[SimpleTypeMapper](header, TypeMapperKindSimple) + _ = castTypeMapper[SimpleTypeMapper](header, TypeMapperKindArray) + ~ +!!! unsafelayout: checked cast must directly pair its concrete target type with its matching constant kind + _ = (*SimpleTypeMapper)(unsafe.Pointer(header)) + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + ~ +!!! unsafelayout: reinterpret compiler records only through checked casts or generated AST dispatch + } + + func freshRecords(c *Checker) *Type { + tagged := &LiteralType{} + c.newType(0, 0, tagged.AsType(), typeKindLiteral) + _ = (&LiteralType{}).AsType() + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + escaped := &LiteralType{} + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + c.newType(0, 0, escaped.AsType(), typeKindLiteral) + _ = escaped.AsType() + header := &ObjectType{} + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + _ = &header.TypeBase + c.newType(0, 0, header.AsType(), typeKindObject) + unused := &ObjectType{} + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + _ = unused + value := LiteralType{} + ~ +!!! unsafelayout: declaring a compiler record by value copies or detaches its tagged header; use a pointer + ~ +!!! unsafelayout: allocate compiler records by pointer so checked construction can tag them + _ = value + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + return new(LiteralType).AsType() + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + } + + func freshMappers() (*TypeMapper, *TypeMapper) { + tagged := &SimpleTypeMapper{} + tagged.kind = TypeMapperKindSimple + untagged := &ArrayTypeMapper{} + ~ +!!! unsafelayout: a fresh mapper must be tagged by the statement after its allocation + return &tagged.TypeMapper, &untagged.TypeMapper + } + + type arena[T any] struct{} + + func (a *arena[T]) New() *T { return new(T) } + + func arenaRecords(c *Checker, literals *arena[LiteralType]) *LiteralType { + c.newType(0, 0, literals.New().AsType(), typeKindLiteral) + data := literals.New() + c.newType(0, 0, data.AsType(), typeKindLiteral) + return literals.New() + ~ +!!! unsafelayout: a fresh compiler record must be checked-constructed before it escapes + } + + func zeroValueRecords() (*LiteralType, *SimpleTypeMapper) { + var literal LiteralType + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + var literals [2]LiteralType + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + _ = literals + ~ +!!! unsafelayout: copying a compiler record by value can detach its tagged header; keep a pointer + var mappers [2]SimpleTypeMapper + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + mappers[0].kind = TypeMapperKindSimple + return &literal, &mappers[0] + ~ +!!! unsafelayout: mapper storage held by value is untagged until an init*TypeMapper function tags it; pass its address there first + } + + func (m *SimpleTypeMapper) Map(t *Type) *Type { return t } + + func initSimpleTypeMapper(m *SimpleTypeMapper) *TypeMapper { + m.kind = TypeMapperKindSimple + return &m.TypeMapper + } + + type mapperOwner struct { + mapperStorage [2]SimpleTypeMapper + } + + func coallocatedMappers(owner *mapperOwner, t *Type) (*TypeMapper, *TypeMapper, *Type) { + tagged := initSimpleTypeMapper(&owner.mapperStorage[0]) + escaped := &owner.mapperStorage[1].TypeMapper + ~ +!!! unsafelayout: mapper storage held by value is untagged until an init*TypeMapper function tags it; pass its address there first + return tagged, escaped, owner.mapperStorage[1].Map(t) + ~ +!!! unsafelayout: mapper storage held by value is untagged until an init*TypeMapper function tags it; pass its address there first + } + + func zeroValueMapper() *TypeMapper { + var m SimpleTypeMapper + ~ +!!! unsafelayout: a zero-value compiler record bypasses checked construction; use its owning factory + return &m.TypeMapper + ~ +!!! unsafelayout: mapper storage held by value is untagged until an init*TypeMapper function tags it; pass its address there first + } + diff --git a/tools/customlint/unsafelayout.go b/tools/customlint/unsafelayout.go new file mode 100644 index 0000000000000..da7a994ec4faa --- /dev/null +++ b/tools/customlint/unsafelayout.go @@ -0,0 +1,823 @@ +package customlint + +import ( + "go/ast" + "go/constant" + "go/token" + "go/types" + "path/filepath" + "slices" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var unsafeLayoutAnalyzer = &analysis.Analyzer{ + Name: "unsafelayout", + Doc: "enforces construction, immutable tags, and checked casts for compiler records", + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: func(pass *analysis.Pass) (any, error) { + in := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + p := unsafeLayoutPass{pass: pass} + p.collectNodeLayouts() + for c := range in.Root().Preorder() { + p.check(c) + if expr, ok := c.Node().(ast.Expr); ok { + p.checkCopy(c, expr) + } + if name, ok := c.Node().(*ast.Ident); ok { + p.checkDeclaration(c, name) + } + } + return nil, nil + }, +} + +type unsafeLayoutPass struct { + pass *analysis.Pass + nodeLayouts map[*types.TypeName][]int64 +} + +func (p *unsafeLayoutPass) collectNodeLayouts() { + if !strings.HasSuffix(p.pass.Pkg.Path(), "/internal/ast") { + return + } + p.nodeLayouts = make(map[*types.TypeName][]int64) + for _, file := range p.pass.Files { + for _, declaration := range file.Decls { + fn, ok := declaration.(*ast.FuncDecl) + if !ok || fn.Recv == nil || !strings.HasPrefix(fn.Name.Name, "As") { + continue + } + function, _ := p.pass.TypesInfo.Defs[fn.Name].(*types.Func) + if function == nil { + continue + } + signature := function.Type().(*types.Signature) + if signature.Results().Len() != 1 { + continue + } + target := layoutNamedType(signature.Results().At(0).Type()) + if target == nil || layoutFamily(target) != "Node" || fn.Name.Name != "As"+target.Obj().Name() { + continue + } + var kinds []int64 + ast.Inspect(fn.Body, func(node ast.Node) bool { + if expr, isIdentifier := node.(*ast.Ident); isIdentifier { + if value, isConstant := p.pass.TypesInfo.Uses[expr].(*types.Const); isConstant && + value.Pkg() == p.pass.Pkg && layoutNamedType(value.Type()) != nil && + layoutNamedType(value.Type()).Obj().Name() == "Kind" { + if kind, isInteger := constant.Int64Val(value.Val()); isInteger { + kinds = append(kinds, kind) + } + } + } + return true + }) + if len(kinds) != 0 { + p.nodeLayouts[target.Obj()] = kinds + } + } + } +} + +func (p *unsafeLayoutPass) matchingNodeKind(typ types.Type, expr ast.Expr) bool { + named := layoutNamedType(typ) + if named == nil { + return false + } + value := p.pass.TypesInfo.Types[expr].Value + if value == nil || value.Kind() != constant.Int { + return false + } + kind, ok := constant.Int64Val(value) + if !ok { + return false + } + return slices.Contains(p.nodeLayouts[named.Obj()], kind) +} + +func layoutNamedType(typ types.Type) *types.Named { + if typ == nil { + return nil + } + if pointer, ok := types.Unalias(typ).(*types.Pointer); ok { + typ = pointer.Elem() + } + return namedType(typ) +} + +func layoutFamily(typ types.Type) string { + named := layoutNamedType(typ) + if named == nil || named.Obj().Pkg() == nil { + return "" + } + path := named.Obj().Pkg().Path() + if strings.HasSuffix(path, "/internal/ast") && (named.Obj().Name() == "Node" || named.Obj().Name() == "MutableNode") { + return "Node" + } + if strings.HasSuffix(path, "/internal/checker") { + switch named.Obj().Name() { + case "Type", "TypeMapper": + return named.Obj().Name() + } + } + if structure, ok := named.Underlying().(*types.Struct); ok && structure.NumFields() != 0 && structure.Field(0).Embedded() { + if _, pointer := types.Unalias(structure.Field(0).Type()).(*types.Pointer); !pointer { + return layoutFamily(structure.Field(0).Type()) + } + } + return "" +} + +// Reports whether typ holds a compiler record by value, directly or inside +// arrays or structs, so declaring it yields untagged zero-value records. +// Mappers coallocated as struct fields are exempt; checkMapperAddress makes +// sure their storage only escapes through a tagging init function. +func containsLayoutRecord(typ types.Type) bool { + return containsRecord(typ, false) +} + +func containsRecord(typ types.Type, field bool) bool { + if family := layoutFamily(typ); family != "" { + _, pointer := types.Unalias(typ).(*types.Pointer) + return !pointer && (family != "TypeMapper" || !field) + } + switch underlying := types.Unalias(typ).Underlying().(type) { + case *types.Array: + return containsRecord(underlying.Elem(), field) + case *types.Struct: + for f := range underlying.Fields() { + if containsRecord(f.Type(), true) { + return true + } + } + } + return false +} + +func isMapperValue(typ types.Type) bool { + if typ == nil { + return false + } + _, pointer := types.Unalias(typ).(*types.Pointer) + return !pointer && layoutFamily(typ) == "TypeMapper" +} + +// Mapper storage held by value starts with a zero tag, so its address may +// only escape as an argument to a checker init*TypeMapper function, which +// tags it. Selecting the embedded base of a mapper reached through a pointer +// is an ordinary upcast of an already-tagged record. +func (p *unsafeLayoutPass) checkMapperAddress(c inspector.Cursor, operand ast.Expr, pos token.Pos, what string) { + operand = ast.Unparen(operand) + if _, ok := operand.(*ast.CompositeLit); ok || !isMapperValue(p.pass.TypesInfo.TypeOf(operand)) { + return + } + if selector, ok := operand.(*ast.SelectorExpr); ok { + if _, pointer := types.Unalias(p.pass.TypesInfo.TypeOf(selector.X)).(*types.Pointer); pointer && + layoutFamily(p.pass.TypesInfo.TypeOf(selector.X)) == "TypeMapper" { + return + } + } + parent := c.Parent() + for { + if _, ok := parent.Node().(*ast.ParenExpr); !ok { + break + } + parent = parent.Parent() + } + if call, ok := parent.Node().(*ast.CallExpr); ok && what == "address" { + var object *types.Func + switch fun := ast.Unparen(call.Fun).(type) { + case *ast.SelectorExpr: + object, _ = p.pass.TypesInfo.Uses[fun.Sel].(*types.Func) + case *ast.Ident: + object, _ = p.pass.TypesInfo.Uses[fun].(*types.Func) + } + if object != nil && object.Pkg() != nil && strings.HasSuffix(object.Pkg().Path(), "/internal/checker") && + strings.HasPrefix(object.Name(), "init") && strings.HasSuffix(object.Name(), "TypeMapper") { + return + } + } + p.pass.Reportf(pos, "mapper storage held by value is untagged until an init*TypeMapper function tags it; pass its address there first") +} + +func layoutKind(typ types.Type) *types.Const { + named := layoutNamedType(typ) + if named == nil || named.Obj().Pkg() == nil { + return nil + } + var name string + switch layoutFamily(typ) { + case "Type": + name = "typeKind" + strings.TrimSuffix(named.Obj().Name(), "Type") + case "TypeMapper": + name = "TypeMapperKind" + strings.TrimSuffix(named.Obj().Name(), "TypeMapper") + default: + return nil + } + kind, _ := named.Obj().Pkg().Scope().Lookup(name).(*types.Const) + return kind +} + +func (p *unsafeLayoutPass) matchingKind(typ types.Type, expr ast.Expr) bool { + kind := layoutKind(typ) + value := p.pass.TypesInfo.Types[expr].Value + return kind != nil && value != nil && types.Identical(kind.Type(), p.pass.TypesInfo.TypeOf(expr)) && + constant.Compare(kind.Val(), token.EQL, value) +} + +func layoutFunction(c inspector.Cursor) *ast.FuncDecl { + for parent := c.Parent(); parent.Node() != nil; parent = parent.Parent() { + switch node := parent.Node().(type) { + case *ast.FuncLit: + return nil + case *ast.FuncDecl: + return node + } + } + return nil +} + +func (p *unsafeLayoutPass) ownerFunction(c inspector.Cursor, family, name string) bool { + fn := layoutFunction(c) + if fn == nil || fn.Name.Name != name { + return false + } + pkg := "/internal/checker" + if family == "Node" { + pkg = "/internal/ast" + } + if !strings.HasSuffix(p.pass.Pkg.Path(), pkg) { + return false + } + function, _ := p.pass.TypesInfo.Defs[fn.Name].(*types.Func) + if function == nil { + return false + } + signature := function.Type().(*types.Signature) + switch name { + case "castType", "castTypeMapper": + return signature.Recv() == nil + case "newNode": + return signature.Recv() == nil || + layoutNamedType(signature.Recv().Type()).Obj().Name() == "NodeFactory" + case "newType": + return signature.Recv() != nil && + layoutNamedType(signature.Recv().Type()).Obj().Name() == "Checker" + default: + return signature.Recv() != nil && layoutNamedType(signature.Recv().Type()).Obj().Name() == family + } +} + +func (p *unsafeLayoutPass) check(c inspector.Cursor) { + node := c.Node() + // Tests use detached metadata stubs; checked-cast pairing still applies. + test := strings.HasSuffix(p.pass.Fset.Position(node.Pos()).Filename, "_test.go") + switch node := node.(type) { + case *ast.TypeSpec: + if !node.Assign.IsValid() && layoutFamily(p.pass.TypesInfo.TypeOf(node.Type)) != "" { + // MutableNode changes the method set without changing the header layout. + if node.Name.Name != "MutableNode" || !strings.HasSuffix(p.pass.Pkg.Path(), "/internal/ast") { + p.pass.Reportf(node.Pos(), "defining a new compiler-record type bypasses checked construction; use a type alias") + } + } + case *ast.ValueSpec: + if test || len(node.Values) != 0 || p.layoutConstant(c) { + return + } + for _, name := range node.Names { + if variable := p.pass.TypesInfo.Defs[name]; variable != nil && containsLayoutRecord(variable.Type()) { + p.pass.Reportf(name.Pos(), "a zero-value compiler record bypasses checked construction; use its owning factory") + } + } + case *ast.IndexExpr: + if function, ok := node.X.(*ast.Ident); ok { + object, _ := p.pass.TypesInfo.Uses[function].(*types.Func) + if object != nil && strings.HasSuffix(object.Pkg().Path(), "/internal/checker") && + (object.Name() == "castType" || object.Name() == "castTypeMapper") { + call, ok := c.Parent().Node().(*ast.CallExpr) + if !ok || call.Fun != node || len(call.Args) != 2 || namedType(p.pass.TypesInfo.TypeOf(node.Index)) == nil || + !p.matchingKind(p.pass.TypesInfo.TypeOf(node.Index), call.Args[1]) { + p.pass.Reportf(node.Pos(), "checked cast must directly pair its concrete target type with its matching constant kind") + } + } + } + case *ast.AssignStmt: + if test { + return + } + for _, lhs := range node.Lhs { + if selector, ok := ast.Unparen(lhs).(*ast.SelectorExpr); ok { + p.checkTagWrite(c, selector, node) + } + } + case *ast.IncDecStmt: + if selector, ok := ast.Unparen(node.X).(*ast.SelectorExpr); ok && p.protectedTag(selector) && !test { + p.pass.Reportf(node.Pos(), "incrementing a compiler tag bypasses checked tag assignment") + } + case *ast.UnaryExpr: + if node.Op == token.AND { + if !test { + p.checkMapperAddress(c, node.X, node.Pos(), "address") + } + if selector, ok := ast.Unparen(node.X).(*ast.SelectorExpr); ok && p.protectedTag(selector) && !test { + p.pass.Reportf(node.Pos(), "taking a compiler tag's address bypasses checked tag assignment") + } + } + case *ast.CompositeLit: + if test || p.layoutConstant(c) { + return + } + if typ := p.pass.TypesInfo.TypeOf(node); layoutFamily(typ) == "" { + if _, slice := typ.Underlying().(*types.Slice); !slice && containsLayoutRecord(typ) { + p.pass.Reportf(node.Pos(), "a zero-value compiler record bypasses checked construction; use its owning factory") + } + return + } + family := layoutFamily(p.pass.TypesInfo.TypeOf(node)) + named := layoutNamedType(p.pass.TypesInfo.TypeOf(node)) + if named.Obj().Name() == family || named.Obj().Name() == "MutableNode" { + p.pass.Reportf(node.Pos(), "a bare compiler header has no concrete payload; use a concrete factory") + } + owner := "/internal/checker" + if family == "Node" { + owner = "/internal/ast" + } + if !strings.HasSuffix(p.pass.Pkg.Path(), owner) { + p.pass.Reportf(node.Pos(), "construct compiler records through their owning factories, not struct literals") + } else if unary, ok := c.Parent().Node().(*ast.UnaryExpr); ok && unary.Op == token.AND { + p.checkFreshRecord(c.Parent(), family) + } else { + p.pass.Reportf(node.Pos(), "allocate compiler records by pointer so checked construction can tag them") + } + for _, element := range node.Elts { + if field, ok := element.(*ast.KeyValueExpr); ok { + if key, ok := field.Key.(*ast.Ident); ok && (key.Name == "kind" || key.Name == "Kind") { + p.pass.Reportf(field.Pos(), "compiler tags must be assigned by checked construction, not struct literals") + } + } + } + case *ast.CallExpr: + p.checkCall(c, node, test) + case *ast.StarExpr, *ast.SelectorExpr, *ast.Ident: + if selector, ok := node.(*ast.SelectorExpr); ok && !test { + if selection := p.pass.TypesInfo.Selections[selector]; selection != nil && selection.Kind() == types.MethodVal { + if signature, ok := selection.Obj().Type().(*types.Signature); ok && signature.Recv() != nil { + if _, pointer := types.Unalias(signature.Recv().Type()).(*types.Pointer); pointer { + p.checkMapperAddress(c, selector.X, selector.Pos(), "method") + } + } + } + } + var object *types.Func + switch expr := node.(type) { + case *ast.SelectorExpr: + object, _ = p.pass.TypesInfo.Uses[expr.Sel].(*types.Func) + case *ast.Ident: + object, _ = p.pass.TypesInfo.Uses[expr].(*types.Func) + } + if object != nil && object.Pkg() != nil && + ((strings.HasSuffix(object.Pkg().Path(), "/internal/checker") && object.Name() == "newType") || + (strings.HasSuffix(object.Pkg().Path(), "/internal/ast") && object.Name() == "newNode")) { + call, ok := c.Parent().Node().(*ast.CallExpr) + if !ok || call.Fun != node { + // The selector's identifier is visited separately. + if selector, ok := c.Parent().Node().(*ast.SelectorExpr); !ok || selector.Sel != node { + p.pass.Reportf(node.Pos(), "checked construction must be called directly so its allocation/tag pairing can be verified") + } + } + } + case *ast.RangeStmt: + if node.Tok == token.ASSIGN && !test { + for _, target := range []ast.Expr{node.Key, node.Value} { + if selector, ok := ast.Unparen(target).(*ast.SelectorExpr); ok && p.protectedTag(selector) { + p.pass.Reportf(selector.Pos(), "range assignment to a compiler tag bypasses checked tag assignment") + } + } + } + } +} + +// Covers receivers, parameters, results, and := and range variables; plain +// var declarations are reported by the zero-value rule. +func (p *unsafeLayoutPass) checkDeclaration(c inspector.Cursor, name *ast.Ident) { + variable, ok := p.pass.TypesInfo.Defs[name].(*types.Var) + if !ok || variable.IsField() || !holdsRecordByValue(variable.Type()) || + strings.HasSuffix(p.pass.Fset.Position(name.Pos()).Filename, "_test.go") { + return + } + if _, ok := c.Parent().Node().(*ast.ValueSpec); ok { + return + } + p.pass.Reportf(name.Pos(), "declaring a compiler record by value copies or detaches its tagged header; use a pointer") +} + +// Reports whether typ is or contains a compiler record by value, including mappers. +func holdsRecordByValue(typ types.Type) bool { + if _, pointer := types.Unalias(typ).(*types.Pointer); pointer { + return false + } + return layoutFamily(typ) != "" || containsLayoutRecord(typ) +} + +// Every value-producing expression holding a record by value is a copy unless it +// is only addressed, has a field or method selected, or is an indexed array. +func (p *unsafeLayoutPass) checkCopy(c inspector.Cursor, expr ast.Expr) { + switch expr.(type) { + case *ast.CompositeLit, *ast.ParenExpr, *ast.FuncLit, *ast.KeyValueExpr: + return + } + if strings.HasSuffix(p.pass.Fset.Position(expr.Pos()).Filename, "_test.go") { + return + } + value, ok := p.pass.TypesInfo.Types[expr] + if !ok || value.IsType() || value.Type == nil || !holdsRecordByValue(value.Type) || p.layoutConstant(c) { + return + } + parent := c.Parent() + for { + if _, ok := parent.Node().(*ast.ParenExpr); !ok { + break + } + parent = parent.Parent() + } + switch parent := parent.Node().(type) { + case *ast.SelectorExpr: + if ast.Unparen(parent.X) == expr { + return + } + case *ast.UnaryExpr: + if parent.Op == token.AND { + return + } + case *ast.IndexExpr: + if _, array := value.Type.Underlying().(*types.Array); array && ast.Unparen(parent.X) == expr { + return + } + case *ast.SliceExpr: + if ast.Unparen(parent.X) == expr { + return + } + } + p.pass.Reportf(expr.Pos(), "copying a compiler record by value can detach its tagged header; keep a pointer") +} + +func (p *unsafeLayoutPass) protectedTag(selector *ast.SelectorExpr) bool { + return p.tagFamily(selector) != "" +} + +func (p *unsafeLayoutPass) tagFamily(selector *ast.SelectorExpr) string { + selection := p.pass.TypesInfo.Selections[selector] + if selection == nil || selection.Obj().Pkg() == nil { + return "" + } + field := selection.Obj() + if field.Name() != "Kind" && field.Name() != "kind" { + return "" + } + for _, family := range []string{"Node", "Type", "TypeMapper"} { + object := field.Pkg().Scope().Lookup(family) + if object == nil || layoutFamily(object.Type()) != family { + continue + } + structure := object.Type().Underlying().(*types.Struct) + for variable := range structure.Fields() { + if variable == field { + return family + } + } + } + return "" +} + +func (p *unsafeLayoutPass) checkTagWrite(c inspector.Cursor, selector *ast.SelectorExpr, assignment *ast.AssignStmt) { + if !p.protectedTag(selector) { + return + } + family := p.tagFamily(selector) + switch family { + case "Node": + if p.ownerFunction(c, family, "newNode") && layoutFunction(c).Recv == nil { + p.checkInitializer(c, selector, assignment) + return + } + p.pass.Reportf(selector.Pos(), "AST kind is immutable outside checked construction; create a new node") + case "Type": + if p.ownerFunction(c, family, "newType") { + p.checkInitializer(c, selector, assignment) + return + } + p.pass.Reportf(selector.Pos(), "type kind is immutable outside checked construction") + case "TypeMapper": + if assignment.Tok == token.ASSIGN && len(assignment.Lhs) == 1 && len(assignment.Rhs) == 1 && + p.matchingKind(p.pass.TypesInfo.TypeOf(selector.X), assignment.Rhs[0]) { + return + } + p.pass.Reportf(selector.Pos(), "mapper tag must match the concrete mapper allocation") + } +} + +// The tag must come from the constructor's own kind parameter, not from a +// shadowing local that happens to share its name. +func (p *unsafeLayoutPass) checkInitializer(c inspector.Cursor, selector *ast.SelectorExpr, assignment *ast.AssignStmt) { + if assignment.Tok == token.ASSIGN && len(assignment.Lhs) == 1 && len(assignment.Rhs) == 1 { + if kind, ok := ast.Unparen(assignment.Rhs[0]).(*ast.Ident); ok { + if function := layoutFunction(c); function != nil && function.Type.Params != nil { + for _, field := range function.Type.Params.List { + for _, name := range field.Names { + if name.Name == "kind" && p.pass.TypesInfo.Uses[kind] == p.pass.TypesInfo.Defs[name] { + return + } + } + } + } + } + } + p.pass.Reportf(selector.Pos(), "checked construction must initialize the tag from its kind parameter") +} + +func (p *unsafeLayoutPass) layoutConstant(c inspector.Cursor) bool { + for parent := c.Parent(); parent.Node() != nil; parent = parent.Parent() { + if call, ok := parent.Node().(*ast.CallExpr); ok { + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + object := p.pass.TypesInfo.Uses[selector.Sel] + if object != nil && object.Pkg() != nil && object.Pkg().Path() == "unsafe" && + (object.Name() == "Offsetof" || object.Name() == "Sizeof") { + return true + } + } + } + } + return false +} + +func (p *unsafeLayoutPass) checkCall(c inspector.Cursor, call *ast.CallExpr, test bool) { + var function *types.Func + switch expr := call.Fun.(type) { + case *ast.SelectorExpr: + function, _ = p.pass.TypesInfo.Uses[expr.Sel].(*types.Func) + case *ast.Ident: + function, _ = p.pass.TypesInfo.Uses[expr].(*types.Func) + } + if function != nil && function.Pkg() != nil && !test { + if strings.HasSuffix(function.Pkg().Path(), "/internal/checker") && function.Name() == "newType" { + valid := len(call.Args) == 4 && p.matchingUpcast(call.Args[2], call.Args[3], "Type", false) + if !valid { + p.pass.Reportf(call.Pos(), "newType must directly pair a concrete AsType() receiver with its matching constant kind") + } + } + if strings.HasSuffix(function.Pkg().Path(), "/internal/ast") && function.Name() == "newNode" && + !p.ownerFunction(c, "Node", "newNode") { + valid := len(call.Args) >= 2 && p.matchingUpcast(call.Args[1], call.Args[0], "Node", p.generatedAST(c)) + if !valid { + p.pass.Reportf(call.Pos(), "newNode must pair a concrete AsNode() receiver with a compatible kind") + } + } + } + if selector, ok := call.Fun.(*ast.SelectorExpr); ok && selector.Sel.Name == "New" && len(call.Args) == 0 && !test { + if family := layoutFamily(p.pass.TypesInfo.TypeOf(call)); family != "" { + p.checkFreshRecord(c, family) + } + } + if builtin, ok := call.Fun.(*ast.Ident); ok && (builtin.Name == "new" || builtin.Name == "make") && !test && len(call.Args) != 0 { + allocated := p.pass.TypesInfo.TypeOf(call.Args[0]) + // new(T) of a record itself is reported with the allocation rules below. + direct := builtin.Name == "new" && layoutFamily(allocated) != "" + if slice, ok := types.Unalias(allocated).(*types.Slice); ok && builtin.Name == "make" { + allocated = slice.Elem() + } + if allocated != nil && !direct && containsLayoutRecord(allocated) { + p.pass.Reportf(call.Pos(), "a zero-value compiler record bypasses checked construction; use its owning factory") + } + } + if builtin, ok := ast.Unparen(call.Fun).(*ast.Ident); ok && !test && len(call.Args) != 0 { + if _, ok := p.pass.TypesInfo.Uses[builtin].(*types.Builtin); ok && + (builtin.Name == "copy" || builtin.Name == "append" && call.Ellipsis.IsValid()) { + // Element-wise copies are invisible to checkCopy because the + // arguments are slices. + if slice, ok := types.Unalias(p.pass.TypesInfo.TypeOf(call.Args[0])).Underlying().(*types.Slice); ok && + holdsRecordByValue(slice.Elem()) { + p.pass.Reportf(call.Pos(), "copying a compiler record by value can detach its tagged header; keep a pointer") + } + } + } + if len(call.Args) != 1 { + return + } + if builtin, ok := call.Fun.(*ast.Ident); ok && builtin.Name == "new" { + family := layoutFamily(p.pass.TypesInfo.TypeOf(call)) + if family != "" && !test { + named := layoutNamedType(p.pass.TypesInfo.TypeOf(call)) + if named.Obj().Name() == family || named.Obj().Name() == "MutableNode" { + p.pass.Reportf(call.Pos(), "a bare compiler header has no concrete payload; use a concrete factory") + } + owner := "/internal/checker" + if family == "Node" { + owner = "/internal/ast" + } + if !strings.HasSuffix(p.pass.Pkg.Path(), owner) { + p.pass.Reportf(call.Pos(), "allocate compiler records through their owning factories") + } else { + p.checkFreshRecord(c, family) + } + } + } + source := p.pass.TypesInfo.TypeOf(call.Args[0]) + target := p.pass.TypesInfo.TypeOf(call) + isUnsafePointer := func(typ types.Type) bool { + if typ == nil { + return false + } + // Aliases and defined types share unsafe.Pointer's conversion rules. + basic, ok := typ.Underlying().(*types.Basic) + return ok && basic.Kind() == types.UnsafePointer + } + if !isUnsafePointer(source) && !isUnsafePointer(target) { + return + } + family := layoutFamily(target) + if family == "" { + family = layoutFamily(source) + } + if family == "" { + if pointer, ok := call.Args[0].(*ast.CallExpr); ok && len(pointer.Args) == 1 { + family = layoutFamily(p.pass.TypesInfo.TypeOf(pointer.Args[0])) + } + } + if family == "" { + return + } + allowed := false + switch family { + case "Node": + allowed = p.generatedAST(c) || p.ownerFunction(c, family, "AsFlowSwitchClauseData") || + p.ownerFunction(c, family, "AsFlowReduceLabelData") + case "Type": + allowed = p.ownerFunction(c, family, "castType") + for _, base := range []string{"ConstrainedType", "StructuredType", "ObjectType", "TypeReference", "InterfaceType", "UnionOrIntersectionType"} { + allowed = allowed || p.ownerFunction(c, family, "As"+base) + } + case "TypeMapper": + allowed = p.ownerFunction(c, family, "castTypeMapper") + } + if !allowed { + p.pass.Reportf(call.Pos(), "reinterpret compiler records only through checked casts or generated AST dispatch") + } +} + +func (p *unsafeLayoutPass) generatedAST(c inspector.Cursor) bool { + for parent := c; parent.Node() != nil; parent = parent.Parent() { + if file, ok := parent.Node().(*ast.File); ok { + return filepath.Base(p.pass.Fset.Position(file.Pos()).Filename) == "ast_generated.go" && ast.IsGenerated(file) && + strings.HasSuffix(p.pass.Pkg.Path(), "/internal/ast") + } + } + return false +} + +func (p *unsafeLayoutPass) matchingUpcast(expr, kind ast.Expr, family string, generated bool) bool { + call, ok := expr.(*ast.CallExpr) + if !ok || len(call.Args) != 0 { + return false + } + method, ok := call.Fun.(*ast.SelectorExpr) + if !ok || method.Sel.Name != "As"+family { + return false + } + selection := p.pass.TypesInfo.Selections[method] + if selection == nil { + return false + } + signature := selection.Obj().Type().(*types.Signature) + receiver := layoutNamedType(signature.Recv().Type()) + if receiver == nil || layoutFamily(receiver) != family || + (receiver.Obj().Name() != family && !(family == "Type" && receiver.Obj().Name() == "TypeBase")) { + return false + } + if family == "Node" { + return p.matchingNodeKind(p.pass.TypesInfo.TypeOf(method.X), kind) || + (generated && p.pass.TypesInfo.Types[kind].Value == nil) + } + return p.matchingKind(p.pass.TypesInfo.TypeOf(method.X), kind) +} + +// A fresh record has a zero tag until checked construction assigns one, so it +// must not escape before then. Types and nodes must be upcast straight into +// newType or newNode, or held in a local that is only used through its own +// fields until that upcast. Mappers must be tagged by the next statement. +func (p *unsafeLayoutPass) checkFreshRecord(alloc inspector.Cursor, family string) { + parent := alloc.Parent() + for { + if _, ok := parent.Node().(*ast.ParenExpr); !ok { + break + } + alloc, parent = parent, parent.Parent() + } + if p.constructorUpcast(alloc, family) { + return + } + assignment, ok := parent.Node().(*ast.AssignStmt) + if !ok || assignment.Tok != token.DEFINE || len(assignment.Lhs) != 1 || len(assignment.Rhs) != 1 { + p.pass.Reportf(alloc.Node().Pos(), "a fresh compiler record must be checked-constructed before it escapes") + return + } + name, _ := assignment.Lhs[0].(*ast.Ident) + variable := p.pass.TypesInfo.Defs[name] + if variable == nil { + p.pass.Reportf(alloc.Node().Pos(), "a fresh compiler record must be checked-constructed before it escapes") + return + } + if family == "TypeMapper" { + if next, ok := parent.NextSibling(); ok { + if tag, ok := next.Node().(*ast.AssignStmt); ok && len(tag.Lhs) == 1 { + if selector, ok := tag.Lhs[0].(*ast.SelectorExpr); ok && p.protectedTag(selector) { + if x, ok := selector.X.(*ast.Ident); ok && p.pass.TypesInfo.Uses[x] == variable { + return + } + } + } + } + p.pass.Reportf(alloc.Node().Pos(), "a fresh mapper must be tagged by the statement after its allocation") + return + } + scope := parent.Parent() + for scope.Node() != nil { + if _, ok := scope.Node().(*ast.BlockStmt); ok { + if _, ok := scope.Parent().Node().(*ast.FuncDecl); ok { + break + } + if _, ok := scope.Parent().Node().(*ast.FuncLit); ok { + break + } + } + scope = scope.Parent() + } + constructed := false + escaped := false + for use := range scope.Preorder((*ast.Ident)(nil)) { + if p.pass.TypesInfo.Uses[use.Node().(*ast.Ident)] != variable { + continue + } + selector, ok := use.Parent().Node().(*ast.SelectorExpr) + if !ok || selector.X != use.Node() { + escaped = true + continue + } + selection := p.pass.TypesInfo.Selections[selector] + switch { + case selection == nil: + escaped = true + case selection.Kind() == types.FieldVal: + if field := selection.Obj().(*types.Var); field.Embedded() { + escaped = true + } + case p.constructorUpcast(use, family): + constructed = true + default: + escaped = true + } + } + if escaped || !constructed { + p.pass.Reportf(alloc.Node().Pos(), "a fresh compiler record must be checked-constructed before it escapes") + } +} + +// Reports whether receiver is upcast directly into the record argument of +// newType or newNode. checkCall separately verifies the kind pairing. +func (p *unsafeLayoutPass) constructorUpcast(receiver inspector.Cursor, family string) bool { + method, ok := receiver.Parent().Node().(*ast.SelectorExpr) + if !ok || method.X != receiver.Node() || method.Sel.Name != "As"+family { + return false + } + upcast, ok := receiver.Parent().Parent().Node().(*ast.CallExpr) + if !ok || upcast.Fun != method { + return false + } + constructor, ok := receiver.Parent().Parent().Parent().Node().(*ast.CallExpr) + if !ok { + return false + } + var name *ast.Ident + switch fun := constructor.Fun.(type) { + case *ast.SelectorExpr: + name = fun.Sel + case *ast.Ident: + name = fun + } + function, _ := p.pass.TypesInfo.Uses[name].(*types.Func) + if function == nil || function.Pkg() == nil { + return false + } + switch family { + case "Type": + return strings.HasSuffix(function.Pkg().Path(), "/internal/checker") && function.Name() == "newType" && + len(constructor.Args) == 4 && constructor.Args[2] == upcast + case "Node": + return strings.HasSuffix(function.Pkg().Path(), "/internal/ast") && function.Name() == "newNode" && + len(constructor.Args) >= 2 && constructor.Args[1] == upcast + } + return false +} diff --git a/tools/scripts/build-wasm-tinygo.mjs b/tools/scripts/build-wasm-tinygo.mjs new file mode 100755 index 0000000000000..9a556269c14a6 --- /dev/null +++ b/tools/scripts/build-wasm-tinygo.mjs @@ -0,0 +1,174 @@ +#!/usr/bin/env node +// Builds tsc into a standalone Wasm module using TinyGo 0.43.0 with the +// repository's runtime/target overlay applied in an isolated TINYGOROOT. +// If TinyGo 0.43.0 is not installed locally, it is automatically downloaded +// from the official GitHub release into /tmp/tinygo-0.43.0-base. + +import { + execFileSync, + spawnSync, +} from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const TINYGO_VERSION = "0.43.0"; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, "../.."); +const tscDir = path.join(repoRoot, "tsc"); +const overlayDir = path.join(__dirname, "testdata", "tinygo-overlay"); + +let outPath = "/tmp/tsgo-tinygo-fast.wasm"; +for (let i = 2; i < process.argv.length; i++) { + const arg = process.argv[i]; + if ((arg === "-o" || arg === "--out") && process.argv[i + 1]) { + outPath = path.resolve(process.argv[++i]); + } + else if (arg.startsWith("--out=")) { + outPath = path.resolve(arg.slice("--out=".length)); + } +} + +function checkTinyGoRoot(candidateRoot) { + if (!candidateRoot) return null; + const bin = path.join(candidateRoot, "bin", "tinygo"); + if (!fs.existsSync(bin)) return null; + const res = spawnSync(bin, ["version"], { encoding: "utf8" }); + if (res.status === 0 && res.stdout.includes(`tinygo version ${TINYGO_VERSION}`)) { + return candidateRoot; + } + return null; +} + +function findOrDownloadBaseTinyGo() { + // 1. Check explicit env override + const fromEnv = checkTinyGoRoot(process.env.TINYGOROOT_BASE); + if (fromEnv) return fromEnv; + + // 2. Check common mise install location + const miseCandidate = path.join( + os.homedir(), + `.local/share/mise/installs/tinygo/${TINYGO_VERSION}/tinygo`, + ); + if (checkTinyGoRoot(miseCandidate)) return miseCandidate; + + // 3. Check tinygo on PATH + const whichRes = spawnSync("tinygo", ["env", "TINYGOROOT"], { encoding: "utf8" }); + if (whichRes.status === 0) { + const pathRoot = checkTinyGoRoot(whichRes.stdout.trim()); + if (pathRoot) return pathRoot; + } + + // 4. Check cached download in /tmp + const cacheDir = path.join(os.tmpdir(), `tinygo-${TINYGO_VERSION}-base`); + const cachedRoot = path.join(cacheDir, "tinygo"); + if (checkTinyGoRoot(cachedRoot)) return cachedRoot; + + // 5. Download official TinyGo 0.43.0 release tarball for current OS/arch + const platformMap = { darwin: "darwin", linux: "linux" }; + const archMap = { arm64: "arm64", x64: "amd64" }; + const plat = platformMap[os.platform()]; + const arch = archMap[os.arch()]; + if (!plat || !arch) { + throw new Error(`Unsupported platform/arch for TinyGo auto-download: ${os.platform()}/${os.arch()}`); + } + const archiveName = `tinygo${TINYGO_VERSION}.${plat}-${arch}.tar.gz`; + const url = `https://github.com/tinygo-org/tinygo/releases/download/v${TINYGO_VERSION}/${archiveName}`; + fs.mkdirSync(cacheDir, { recursive: true }); + const tarPath = path.join(cacheDir, archiveName); + console.log(`Downloading TinyGo ${TINYGO_VERSION} from ${url}...`); + execFileSync("curl", ["-fSL", "--retry", "3", "-o", tarPath, url], { stdio: "inherit" }); + execFileSync("tar", ["-xzf", tarPath, "-C", cacheDir], { stdio: "inherit" }); + if (!checkTinyGoRoot(cachedRoot)) { + throw new Error(`Failed to verify TinyGo ${TINYGO_VERSION} in ${cachedRoot}`); + } + return cachedRoot; +} + +function buildOverlayTinyGoRoot(baseRoot) { + const workRoot = path.join(os.tmpdir(), `tinygo-${TINYGO_VERSION}-tsgo-overlay`); + fs.rmSync(workRoot, { recursive: true, force: true }); + fs.mkdirSync(workRoot, { recursive: true }); + + // Symlink top-level immutable directories (bin, lib, pkg, targets) + for (const entry of fs.readdirSync(baseRoot)) { + if (entry === "src") continue; + fs.symlinkSync(path.join(baseRoot, entry), path.join(workRoot, entry)); + } + + // Copy src/ (except the 900MB src/device directory, which is symlinked), + // then copy the overlay files on top. + fs.mkdirSync(path.join(workRoot, "src"), { recursive: true }); + for (const entry of fs.readdirSync(path.join(baseRoot, "src"))) { + if (entry === "device") { + fs.symlinkSync(path.join(baseRoot, "src", entry), path.join(workRoot, "src", entry)); + } + else { + fs.cpSync(path.join(baseRoot, "src", entry), path.join(workRoot, "src", entry), { recursive: true }); + } + } + fs.cpSync(overlayDir, workRoot, { recursive: true, force: true }); + + return workRoot; +} + +const baseRoot = findOrDownloadBaseTinyGo(); +const overlayRoot = buildOverlayTinyGoRoot(baseRoot); +const tinygoBin = path.join(overlayRoot, "bin", "tinygo"); + +console.log(`Using TinyGo ${TINYGO_VERSION} (base: ${baseRoot}, overlay: ${overlayRoot})`); +console.log(`Building ${outPath}...`); + +const t0 = Date.now(); +execFileSync( + tinygoBin, + [ + "build", + "-target=wasip1", + "-buildmode=c-shared", + "-opt=2", + "-scheduler=none", + "-gc=leaking", + "-no-debug", + "-ldflags=-extldflags '-z stack-size=16777216'", + "-o", + outPath, + "./cmd/tsc", + ], + { + cwd: tscDir, + env: { ...process.env, TINYGOROOT: overlayRoot }, + stdio: "inherit", + }, +); +function findWasmOpt() { + const res = spawnSync("wasm-opt", ["--version"], { encoding: "utf8" }); + if (res.status === 0) return "wasm-opt"; + for (const candidate of ["/opt/homebrew/bin/wasm-opt", "/usr/local/bin/wasm-opt"]) { + if (fs.existsSync(candidate)) return candidate; + } + return null; +} + +const wasmOptBin = findWasmOpt(); +if (wasmOptBin) { + execFileSync( + wasmOptBin, + [ + "-O3", + "--enable-bulk-memory", + "--enable-sign-ext", + "--enable-mutable-globals", + "--enable-nontrapping-float-to-int", + "--enable-simd", + outPath, + "-o", + outPath, + ], + { stdio: "inherit" }, + ); +} + +const stat = fs.statSync(outPath); +console.log(`Built ${outPath} (${(stat.size / (1024 * 1024)).toFixed(2)} MiB) in ${((Date.now() - t0) / 1000).toFixed(1)}s`); diff --git a/tools/scripts/gen/cache.mts b/tools/scripts/gen/cache.mts index 99b9e6e3efb1f..d4505f9113083 100644 --- a/tools/scripts/gen/cache.mts +++ b/tools/scripts/gen/cache.mts @@ -3,11 +3,11 @@ import * as fs from "node:fs"; import path from "node:path"; import { GeneratedFile } from "./generatedFile.mts"; import { - formatCommandArg, getFileFingerprint, goInputs, repoRoot, run, + withFileFingerprintWrites, } from "./utils.mts"; export interface CacheOptions { @@ -21,6 +21,10 @@ export interface CacheOptions { force?: boolean; } +export interface CacheHost { + run(command: string, args: readonly string[], options: { cwd: string; env: NodeJS.ProcessEnv; }): Promise; +} + function expand(patterns: string[], cwd: string, exclude: string[] = []) { return [...new Set(fs.globSync(patterns, { cwd, exclude }).map(file => path.resolve(cwd, file)))].sort(); } @@ -34,7 +38,7 @@ export default async function cache({ envInputs = ["GOOS", "GOARCH", "GOFLAGS", "GOTOOLCHAIN", "GOEXPERIMENT", "CGO_ENABLED", "GOWORK"], env, force = false, -}: CacheOptions): Promise { +}: CacheOptions, host: CacheHost = { run }): Promise { if (!outputs.length || !commands.length || commands.some(command => !command.length)) { throw new Error("Cached generation requires outputs and nonempty commands."); } @@ -85,15 +89,15 @@ export default async function cache({ ); const previous = artifacts(outputFiles()); const complete = () => outputs.every(pattern => expand([pattern], cwd).some(file => fs.statSync(file).isFile())); - const commandText = commands.map(command => command.map(formatCommandArg).join(" ")).join("; "); if (complete() && previous.every(file => file.isCurrent(force))) { - console.log(`skipped ${commandText}: codegen outputs are already up to date`); return true; } for (const file of previous) file.invalidate(); - for (const [command, ...args] of commands) { - await run(command, args, { cwd, env: environment }); - } + await withFileFingerprintWrites(async () => { + for (const [command, ...args] of commands) { + await host.run(command, args, { cwd, env: environment }); + } + }); if (!complete()) throw new Error(`Generation did not produce all declared outputs: ${outputs.join(", ")}`); if (snapshotInputs(true).hash === before.hash) { for (const file of artifacts(outputFiles())) file.markCurrent(); diff --git a/tools/scripts/gen/cache.test.mts b/tools/scripts/gen/cache.test.mts index 4dd87ed158687..7898fdd7c500c 100644 --- a/tools/scripts/gen/cache.test.mts +++ b/tools/scripts/gen/cache.test.mts @@ -1,59 +1,141 @@ import assert from "node:assert/strict"; -import * as fs from "node:fs"; +import fs from "node:fs"; +import { syncBuiltinESMExports } from "node:module"; import os from "node:os"; import path from "node:path"; import { test } from "node:test"; -import cache from "./cache.mts"; +import cache, { type CacheHost } from "./cache.mts"; +import { GeneratedFile } from "./generatedFile.mts"; +import { enableFileFingerprintCache } from "./utils.mts"; + +test("cached commands produce no output", async context => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache-quiet-")); + context.after(() => fs.rmSync(directory, { recursive: true, force: true })); + const options = { + cwd: directory, + inputs: [], + outputs: ["output.txt"], + commands: [["generate"]], + }; + const host: CacheHost = { + async run() { + fs.writeFileSync(path.join(directory, "output.txt"), "generated"); + }, + }; + assert.equal(await cache(options, host), false); + const log = context.mock.method(console, "log", () => {}); + assert.equal(await cache(options, host), true); + assert.equal(log.mock.callCount(), 0); +}); + +test("command cache snapshots and generated files share invocation fingerprints", async context => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-fingerprint-")); + context.after(() => fs.rmSync(directory, { recursive: true, force: true })); + const input = path.join(directory, "input.txt"); + fs.writeFileSync(input, "input"); + const host: CacheHost = { + async run() { + fs.writeFileSync(path.join(directory, "output.txt"), "generated"); + }, + }; + const options = { + cwd: directory, + inputs: ["input.txt"], + outputs: ["output.txt"], + commands: [["generate"]], + }; + assert.equal(await cache(options, host), false); + context.after(enableFileFingerprintCache()); + const reads = context.mock.method(fs, "readFileSync"); + syncBuiltinESMExports(); + context.after(() => { + reads.mock.restore(); + syncBuiltinESMExports(); + }); + assert.equal(await cache(options, host), true); + assert.equal(await cache(options, host), true); + new GeneratedFile(path.join(directory, "other.txt"), [input]); + assert.equal(reads.mock.calls.filter(call => call.arguments[0] === input).length, 1); +}); test("cache commands use and track an explicit environment without changing the parent", async context => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache-env-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true })); const parentValue = process.env.GOFILE; + let calls = 0; + const host: CacheHost = { + async run(command, args, { cwd, env }) { + calls++; + assert.equal(command, "generate"); + assert.deepEqual(args, []); + fs.writeFileSync(path.join(cwd, "output.json"), JSON.stringify({ file: env.GOFILE, pkg: env.GOPACKAGE, cwd })); + }, + }; const options = { cwd: directory, inputs: [], outputs: ["output.json"], envInputs: ["GOFILE", "GOPACKAGE"], env: { GOFILE: "source.go", GOPACKAGE: "example" }, - commands: [[process.execPath, "-e", "require('node:fs').writeFileSync('output.json', JSON.stringify({ file: process.env.GOFILE, pkg: process.env.GOPACKAGE, cwd: process.cwd() }))"]], + commands: [["generate"]], }; - assert.equal(await cache(options), false); + assert.equal(await cache(options, host), false); assert.deepEqual(JSON.parse(fs.readFileSync(path.join(directory, "output.json"), "utf8")), { file: "source.go", pkg: "example", cwd: fs.realpathSync(directory), }); - assert.equal(await cache(options), true); - assert.equal(await cache({ ...options, env: { ...options.env, GOFILE: "other.go" } }), false); + assert.equal(await cache(options, host), true); + assert.equal(calls, 1); + assert.equal(await cache({ ...options, env: { ...options.env, GOFILE: "other.go" } }, host), false); + assert.equal(calls, 2); assert.equal(process.env.GOFILE, parentValue); }); test("cache commands preserve the working directory and arguments", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_cache probe-")); + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache probe-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true, maxRetries: 10 })); fs.writeFileSync(path.join(directory, "input.txt"), "input"); - fs.writeFileSync(path.join(directory, "generate.cjs"), "const fs = require('node:fs'); fs.writeFileSync('output.txt', fs.readFileSync('input.txt') + JSON.stringify(process.argv.slice(2)));\n"); - fs.writeFileSync(path.join(directory, "finish.cjs"), "require('node:fs').appendFileSync('output.txt', ' finished');\n"); + const calls: string[] = []; + const host: CacheHost = { + async run(command, args, { cwd }) { + calls.push(command); + assert.equal(cwd, directory); + if (command === "generate") { + assert.deepEqual(args, ["--help", "--force", "space value"]); + fs.writeFileSync(path.join(cwd, "output.txt"), fs.readFileSync(path.join(cwd, "input.txt")) + JSON.stringify(args)); + } + else { + assert.deepEqual(args, []); + if (command === "fail") throw new Error("Command failed"); + fs.appendFileSync(path.join(cwd, "output.txt"), " finished"); + } + }, + }; const generate = (force = false, failure = false) => cache({ cwd: directory, - inputs: ["input.txt", "*.cjs"], + inputs: ["input.txt"], outputs: ["output.txt"], commands: [ - [process.execPath, "generate.cjs", "--help", "--force", "space value"], - [process.execPath, failure ? "missing.cjs" : "finish.cjs"], + ["generate", "--help", "--force", "space value"], + [failure ? "fail" : "finish"], + ["last"], ], force, - }); + }, host); const output = path.join(directory, "output.txt"); assert.equal(await generate(), false); - assert.equal(fs.readFileSync(output, "utf8"), 'input["--help","--force","space value"] finished'); - const timestamp = fs.statSync(output).mtimeMs; + assert.equal(fs.readFileSync(output, "utf8"), 'input["--help","--force","space value"] finished finished'); + assert.deepEqual(calls, ["generate", "finish", "last"]); + calls.length = 0; assert.equal(await generate(), true); - assert.equal(fs.statSync(output).mtimeMs, timestamp); + assert.deepEqual(calls, []); assert.equal(await generate(true), false); - await assert.rejects(generate(false, true)); + assert.deepEqual(calls, ["generate", "finish", "last"]); + calls.length = 0; + await assert.rejects(generate(false, true), /Command failed/); + assert.deepEqual(calls, ["generate", "fail"]); assert.equal(await generate(), false); assert.equal(await generate(), true); }); @@ -64,26 +146,38 @@ test("cache commands track inputs, outputs, arguments, and failures", async cont const input = path.join(directory, "input.txt"); const output = path.join(directory, "output.txt"); fs.writeFileSync(input, "first"); - fs.writeFileSync(path.join(directory, "generate.cjs"), "const fs = require('node:fs'); fs.writeFileSync('output.txt', fs.readFileSync('input.txt'));\n"); - fs.writeFileSync(path.join(directory, "finish.cjs"), "const fs = require('node:fs'); fs.appendFileSync('output.txt', process.argv[2]); fs.writeFileSync('extra.txt', 'extra');\n"); + const host: CacheHost = { + async run(command, args, { cwd }) { + if (command === "generate") fs.writeFileSync(output, fs.readFileSync(input)); + else if (command === "fail") throw new Error("Command failed"); + else { + fs.appendFileSync(output, args[0]); + fs.writeFileSync(path.join(cwd, "extra.txt"), "extra"); + } + }, + }; const generate = (suffix = " done", force = false, failure = false, env: NodeJS.ProcessEnv = {}) => cache({ cwd: directory, - inputs: ["input*.txt", "*.cjs"], + inputs: ["input*.txt"], + exclude: ["input-ignored.txt"], outputs: ["output.txt", "extra*.txt"], envInputs: ["TSGO_CACHE_PROBE"], env, commands: [ - [process.execPath, "generate.cjs"], - [process.execPath, failure ? "missing.cjs" : "finish.cjs", suffix], + ["generate"], + [failure ? "fail" : "finish", suffix], ], force, - }); + }, host); await generate(); assert.equal(fs.readFileSync(output, "utf8"), "first done"); const timestamp = fs.statSync(output).mtimeMs; assert.equal(await generate(), true); assert.equal(fs.statSync(output).mtimeMs, timestamp); + fs.writeFileSync(path.join(directory, "input-ignored.txt"), "ignored"); + fs.writeFileSync(path.join(directory, "unrelated.txt"), "unrelated"); + assert.equal(await generate(), true); fs.writeFileSync(input, "second"); await generate(); assert.equal(fs.readFileSync(output, "utf8"), "second done"); @@ -113,40 +207,77 @@ test("cache commands track inputs, outputs, arguments, and failures", async cont assert.equal(await generate("--force"), true); }); +test("failed commands invalidate metadata even when outputs are unchanged", async context => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache-")); + context.after(() => fs.rmSync(directory, { recursive: true, force: true })); + let fail = false; + let calls = 0; + const host: CacheHost = { + async run() { + calls++; + fs.writeFileSync(path.join(directory, "output.txt"), "generated"); + if (fail) throw new Error("Command failed"); + }, + }; + const options = { cwd: directory, inputs: [], outputs: ["output.txt"], commands: [["generate"]] }; + assert.equal(await cache(options, host), false); + fail = true; + await assert.rejects(cache({ ...options, force: true }, host), /Command failed/); + fail = false; + assert.equal(await cache(options, host), false); + assert.equal(await cache(options, host), true); + assert.equal(calls, 3); +}); + test("cache commands reject missing inputs, outputs, and empty commands", async context => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true })); + let calls = 0; + const host: CacheHost = { + async run() { + calls++; + fs.writeFileSync(path.join(directory, "output.txt"), "generated"); + }, + }; const options = { cwd: directory, inputs: ["input.txt"], outputs: ["output.txt"], - commands: [[process.execPath, "-e", "require('node:fs').writeFileSync('output.txt', 'generated')"]], + commands: [["generate"]], }; - await assert.rejects(cache(options), /No inputs matched/); + await assert.rejects(cache(options, host), /No inputs matched/); assert.equal(fs.existsSync(path.join(directory, "output.txt")), false); fs.writeFileSync(path.join(directory, "input.txt"), "input"); - await assert.rejects(cache({ ...options, inputs: ["missing*.txt"] }), /No inputs matched/); - await assert.rejects(cache({ ...options, commands: [] }), /nonempty commands/); - await assert.rejects(cache({ ...options, commands: [[]] }), /nonempty commands/); - await assert.rejects(cache({ ...options, outputs: [] }), /requires outputs/); - await assert.rejects(cache({ ...options, outputs: ["output.txt", "missing.txt"] }), /did not produce all declared outputs/); - assert.equal(await cache(options), false); - assert.equal(await cache(options), true); + await assert.rejects(cache({ ...options, inputs: ["missing*.txt"] }, host), /No inputs matched/); + await assert.rejects(cache({ ...options, commands: [] }, host), /nonempty commands/); + await assert.rejects(cache({ ...options, commands: [[]] }, host), /nonempty commands/); + await assert.rejects(cache({ ...options, outputs: [] }, host), /requires outputs/); + assert.equal(calls, 0); + await assert.rejects(cache({ ...options, outputs: ["output.txt", "missing.txt"] }, host), /did not produce all declared outputs/); + assert.equal(calls, 1); + assert.equal(await cache(options, host), false); + assert.equal(await cache(options, host), true); }); test("cache commands do not cache inputs changed during execution", async context => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-cache-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - for (const change of ["fs.writeFileSync('input.txt', 'changed')", "fs.writeFileSync('input-added.txt', 'added')"]) { + for (const changedFile of ["input.txt", "input-added.txt"]) { fs.writeFileSync(path.join(directory, "input.txt"), "initial"); + const host: CacheHost = { + async run() { + fs.writeFileSync(path.join(directory, "output.txt"), "generated"); + fs.writeFileSync(path.join(directory, changedFile), "changed"); + }, + }; const options = { cwd: directory, inputs: ["input*.txt"], outputs: ["output.txt"], - commands: [[process.execPath, "-e", `const fs = require('node:fs'); fs.writeFileSync('output.txt', 'generated'); ${change};`]], + commands: [["generate"]], }; - assert.equal(await cache(options), false); - assert.equal(await cache(options), false); - assert.equal(await cache(options), true); + assert.equal(await cache(options, host), false); + assert.equal(await cache(options, host), false); + assert.equal(await cache(options, host), true); } }); diff --git a/tools/scripts/gen/generatedFile.test.mts b/tools/scripts/gen/generatedFile.test.mts index eaf918e759fc3..bd85f8b03b536 100644 --- a/tools/scripts/gen/generatedFile.test.mts +++ b/tools/scripts/gen/generatedFile.test.mts @@ -5,11 +5,6 @@ import { syncBuiltinESMExports } from "node:module"; import os from "node:os"; import path from "node:path"; import { test } from "node:test"; -import { stripVTControlCharacters } from "node:util"; -import { runInNewContext } from "node:vm"; -import { x } from "tinyexec"; -import ts from "typescript"; -import cache from "./cache.mts"; import { defaultCacheDirectory, GeneratedFile, @@ -18,70 +13,48 @@ import { enableFileFingerprintCache, getFileFingerprint, repoRoot, - run, + withFileFingerprintWrites, } from "./utils.mts"; -test("validate generates before building and selects the generation scope", async () => { - const fileName = path.resolve(import.meta.dirname, "../../../Herebyfile.mjs"); - const source = ts.createSourceFile(fileName, fs.readFileSync(fileName, "utf8"), ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); - const declaration = source.statements.filter(ts.isVariableStatement) - .flatMap(statement => statement.declarationList.declarations) - .find(declaration => declaration.name.getText(source) === "validate"); - assert.ok(declaration?.initializer); - for (const options of [{}, { api: true }, { all: true }]) { - const calls: string[] = []; - const action = (name: string) => async () => { - calls.push(name); - }; - const generate = { run: action("generate") }; - const generateGo = { run: action("generate:go") }; - const build = { run: action("build") }; - const validation = runInNewContext(declaration.initializer.getText(source), { - task: (spec: unknown) => spec, - options, - generate, - generateGo, - build, - builtLocal: "./built/local", - generateLibs: action("lib"), - buildTsc: action("build"), - getReleaseBuildFlags: () => [], - runGenerateGo: action("generate:go"), - runGenerateEnums: action("generate:enums"), - runGenerateAPI: action("generate:api"), - runGenerateExtension: action("generate:extension"), - runGenerateVendor: action("generate:vendor"), - runBuildAPITests: async (generate = true) => { - if (generate) calls.push("generate:sync"); - calls.push("build:api:test"); - }, - runTests: action("test:tsc"), - runTestExtension: action("test:extension"), - runCheckAPIDeclarations: action("check:api-declarations"), - runTestAPI: action("test:api"), - runTestBenchmarks: action("test:benchmarks"), - runTestTools: action("test:tools"), - run: async (command: string, args: string[]) => { - assert.equal(command, "node"); - assert.deepEqual(Array.from(args), ["--test", "./tools/scripts/tsc/options.test.ts"]); - calls.push("test:options"); - }, - runSmokeTest: action("test:smoke"), - runLint: action("lint"), - runFormat: action("format"), - }) as { dependencies: { run: () => Promise; }[]; run: () => Promise; }; - await Promise.all(validation.dependencies.map(dependency => dependency.run())); - await validation.run(); - assert.equal(calls[0], "all" in options ? "generate" : "generate:go"); - assert.ok(calls.indexOf("build") > 0); - assert.ok(calls.indexOf("build") < calls.indexOf("test:tsc")); - assert.ok(calls.indexOf("check:api-declarations") > calls.indexOf("test:extension")); - assert.equal(calls.includes("test:api"), "api" in options || "all" in options); - assert.equal(calls.includes("test:tools"), "all" in options); - assert.equal(calls.includes("test:options"), "all" in options); - if ("all" in options) { - assert.deepEqual(calls.filter(name => name.startsWith("generate")), ["generate"]); - } +test("unchanged generators produce no output", async context => { + const generators = [ + ["Go AST", (await import("../tsc/generate-go-ast.ts")).default], + ["TypeScript AST", (await import("../tsc/generate-ts-ast.ts")).default], + ["encoder", (await import("../tsc/generate-encoder.ts")).default], + ["enums", (await import("../tsc/generate-enums.ts")).default], + ["Unicode", (await import("../../../tsc/internal/stringutil/_scripts/generate-unicode-data.mts")).default], + ["sync API", (await import("../../../packages/typescript/scripts/generateSync.ts")).generateSync], + ] as const; + for (const [name, generate] of generators) { + await context.test(name, async context => { + context.mock.method(GeneratedFile.prototype, "isCurrent", () => true); + const log = context.mock.method(console, "log", () => {}); + await generate(); + assert.equal(log.mock.callCount(), 0); + }); + } +}); + +test("Go AST generator validates bases after schema reloads", async context => { + const { default: generateGoAST } = await import("../tsc/generate-go-ast.ts"); + const schema = await import("../tsc/schema.ts"); + context.mock.method(GeneratedFile.prototype, "isCurrent", () => true); + context.after(() => schema.reloadSchema()); + generateGoAST(); + + for ( + const { name, extendsKeys, error } of [ + { name: "duplicate bases", extendsKeys: ["NodeBase", "ExpressionBase"], error: /declares duplicate embedded bases/ }, + { name: "missing NodeBase", extendsKeys: [], error: /does not embed NodeBase at offset zero/ }, + ] + ) { + await context.test(name, context => { + schema.reloadSchema(); + const node = [...schema.api.nodes()][0]; + assert.ok(node); + context.mock.getter(node, "extendsKeys", () => extendsKeys); + assert.throws(() => generateGoAST(), error); + }); } }); @@ -170,7 +143,7 @@ test("generated files share input fingerprints and refresh changed inputs", cont assert.equal(new GeneratedFile(output, [input], cache).isCurrent(), true); }); -test("invocation fingerprints reuse metadata and invalidate writes and commands", async context => { +test("invocation fingerprints reuse metadata and invalidate writes", async context => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-fingerprint-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true })); context.after(enableFileFingerprintCache()); @@ -206,21 +179,26 @@ test("invocation fingerprints reuse metadata and invalidate writes and commands" assert.equal(new GeneratedFile(output, [input], metadata).isCurrent(), false); for (const failure of [false, true]) { const before = getFileFingerprint(input); - const content = failure ? "failed command" : "successful command"; - const command = run(process.execPath, ["-e", `require('node:fs').writeFileSync(process.argv[1], ${JSON.stringify(content)}); process.exit(${failure ? 1 : 0});`, input]); - if (failure) await assert.rejects(command); - else await command; + const content = failure ? "failed write" : "successful write"; + const writing = withFileFingerprintWrites(() => { + fs.writeFileSync(input, content); + if (failure) throw new Error("Write failed"); + }); + if (failure) await assert.rejects(writing, /Write failed/); + else await writing; assert.notEqual(getFileFingerprint(input), before); assert.equal(getFileFingerprint(input), createHash("sha256").update(content).digest("hex")); } - const running = run(process.execPath, ["-e", "process.exit(0)"]); + const finished = Promise.withResolvers(); + const writing = withFileFingerprintWrites(() => finished.promise); try { const before = getFileFingerprint(input); - fs.writeFileSync(input, "changed while a command is running"); + fs.writeFileSync(input, "changed while a writer is active"); assert.notEqual(getFileFingerprint(input), before); } finally { - await running; + finished.resolve(); + await writing; } }); @@ -256,31 +234,6 @@ test("fingerprint cache scopes can overlap", context => { } }); -test("command cache snapshots and generated files share invocation fingerprints", async context => { - const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-fingerprint-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - const input = path.join(directory, "input.txt"); - fs.writeFileSync(input, "input"); - const options = { - cwd: directory, - inputs: ["input.txt"], - outputs: ["output.txt"], - commands: [[process.execPath, "-e", "require('node:fs').writeFileSync('output.txt', 'generated')"]], - }; - assert.equal(await cache(options), false); - context.after(enableFileFingerprintCache()); - const reads = context.mock.method(fs, "readFileSync"); - syncBuiltinESMExports(); - context.after(() => { - reads.mock.restore(); - syncBuiltinESMExports(); - }); - assert.equal(await cache(options), true); - assert.equal(await cache(options), true); - new GeneratedFile(path.join(directory, "other.txt"), [input]); - assert.equal(reads.mock.calls.filter(call => call.arguments[0] === input).length, 1); -}); - test("changing inputs during generation does not mark stale output current", context => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "tsgo-codegen-")); context.after(() => fs.rmSync(directory, { recursive: true, force: true })); @@ -351,578 +304,3 @@ test("generated files track each output independently", context => { assert.equal(files[0].isCurrent(), true); assert.equal(files[1].isCurrent(), false); }); - -test("generate:go runs Go generators directly and shares caches with Go fallback", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = () => x("npx", ["hereby", "generate:go"], { throwOnError: true, nodeOptions: { cwd: root } }); - const first = await generate(); - assert.doesNotMatch(first.stdout, /\$ go generate|npm run --silent cache|\$ node .*generate-unicode-data/); - const files = fs.globSync(["tsc/internal/**/*generated.go", "packages/typescript/src/api/*.generated.ts", "packages/typescript/enums/*.{js,ts}", "packages/typescript/schemas/*.schema.json"], { cwd: root }); - const timestamps = files.map(file => fs.statSync(path.join(root, file)).mtimeMs); - const current = await generate(); - assert.doesNotMatch(current.stdout, /Generated codegen outputs|Generated Unicode tables/); - assert.match(current.stdout, /Unicode tables are up to date/); - assert.deepEqual(files.map(file => fs.statSync(path.join(root, file)).mtimeMs), timestamps); - const fallback = await x("go", ["-C", "./tsc", "generate", "./internal/diagnostics"], { throwOnError: true, nodeOptions: { cwd: root } }); - assert.equal(fallback.stdout.match(/codegen outputs are already up to date/g)?.length, 2); - const nested = await x("npx", ["hereby", "generate:compileroptions"], { throwOnError: true, nodeOptions: { cwd: path.join(root, "tsc/internal/core") } }); - assert.equal(nested.stdout.match(/codegen outputs are already up to date/g)?.length, 4); - assert.match(nested.stdout, /Enums are up to date/); -}); - -test("generate:go generates new option diagnostics before Go-dependent generators", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = async () => { - const result = await x("npx", ["hereby", "generate:go"], { nodeOptions: { cwd: root } }); - assert.equal(result.exitCode, 0, result.stdout + result.stderr); - return result; - }; - await generate(); - const metadataFile = path.join(root, "tools/scripts/tsc/options.ts"); - const diagnosticsFile = path.join(root, "tsc/internal/diagnostics/diagnosticMessages.json"); - const protocolFile = path.join(root, "packages/typescript/src/api/proto.generated.ts"); - const originalMetadata = fs.readFileSync(metadataFile, "utf8"); - const originalDiagnostics = fs.readFileSync(diagnosticsFile, "utf8"); - const originalProtocol = fs.readFileSync(protocolFile, "utf8"); - const messages: Record = JSON.parse(originalDiagnostics); - const message = "Codegen ordering probe"; - assert.equal(messages[message], undefined); - messages[message] = { category: "Message", code: Math.max(...Object.values(messages).map(message => message.code)) + 1 }; - const metadata = originalMetadata.replace( - 'diagnostic("Print all of the files read during the compilation.")', - `diagnostic("${message}")`, - ); - assert.notEqual(metadata, originalMetadata); - try { - fs.writeFileSync(diagnosticsFile, JSON.stringify(messages, null, 4) + "\n"); - fs.writeFileSync(metadataFile, metadata); - // Exercise protocol generation too, rather than accepting its cached output. - fs.writeFileSync(protocolFile, originalProtocol + "\n"); - const { stdout } = await generate(); - assert.match(stdout, /All generated values match Go/); - assert.match(stdout, /\$ go -C \.\/tools run \.\/gen-proto/); - assert.equal(fs.readFileSync(protocolFile, "utf8"), originalProtocol); - assert.match(fs.readFileSync(path.join(root, "tsc/internal/tsoptions/declarations_generated.go"), "utf8"), /diagnostics\.Codegen_ordering_probe/); - assert.match(fs.readFileSync(path.join(root, "tsc/internal/diagnostics/diagnostics_generated.go"), "utf8"), /Codegen_ordering_probe/); - } - finally { - fs.writeFileSync(metadataFile, originalMetadata); - fs.writeFileSync(diagnosticsFile, originalDiagnostics); - await generate(); - } -}); - -test("generate includes standalone generators without Go traversal", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const { stdout } = await x("npx", ["hereby", "generate"], { throwOnError: true, nodeOptions: { cwd: root } }); - const log = stripVTControlCharacters(stdout); - const eventIndex = (event: string) => { - const index = log.indexOf(event); - assert.ok(index >= 0, `Missing task event: ${event}`); - return index; - }; - const compilerStart = eventIndex("Starting generate:compiler"); - assert.ok(eventIndex("Finished generate:ast ") < compilerStart); - assert.ok(eventIndex("Finished generate:lsp ") < compilerStart); - assert.ok(eventIndex("Starting generate:sync") < compilerStart); - assert.ok(eventIndex("Starting generate:vendor") < compilerStart); - assert.ok(eventIndex("Finished generate:extension ") < eventIndex("Starting generate:extension-test")); - assert.doesNotMatch(stdout, /\$ go generate|npm run --silent cache/); - assert.match(stdout, /Unicode tables/); - assert.match(stdout, /Enums are up to date|All generated values match Go/); - assert.match(stdout, /Sync API is up to date|Formatting\.\.\./); - assert.match(stdout, /LSP bindings are up to date|Successfully generated .*lsp_generated\.go/); - assert.match(stdout, /generateLocBundle/); - assert.match(stdout, /generateLocTest/); - assert.deepEqual( - fs.readFileSync(path.join(root, "packages/typescript/vendor/vscode-jsonrpc/package.json")), - fs.readFileSync(path.join(root, "node_modules/vscode-jsonrpc/package.json")), - ); - const lspOutputs = ["tsc/internal/lsp/lsproto/lsp_generated.go", "packages/typescript/src/vscode/protocol.generated.ts"].map(file => path.join(root, file)); - const timestamps = lspOutputs.map(file => fs.statSync(file).mtimeMs); - const current = await x("npx", ["hereby", "generate:lsp"], { throwOnError: true, nodeOptions: { cwd: root } }); - assert.match(current.stdout, /LSP bindings are up to date/); - assert.doesNotMatch(current.stdout, /Using vscode-languageclient/); - assert.deepEqual(lspOutputs.map(file => fs.statSync(file).mtimeMs), timestamps); -}); - -test("localization and vendoring preserve current outputs", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = (force = false) => x("npx", ["hereby", "generate:extension-test", "generate:vendor", ...(force ? ["--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const outputs = fs.globSync([ - "packages/vscode-typescript/l10n/*.json", - "packages/vscode-typescript/package.nls.qps-ploc.json", - "packages/typescript/vendor/vscode-jsonrpc/**/*", - ], { cwd: root }).map(file => path.join(root, file)).filter(file => fs.statSync(file).isFile()); - const contents = outputs.map(file => fs.readFileSync(file)); - context.after(() => { - for (const [index, file] of outputs.entries()) fs.writeFileSync(file, contents[index]); - }); - const oldTime = new Date("2000-01-01T00:00:00Z"); - for (const file of outputs) fs.utimesSync(file, oldTime, oldTime); - const timestamps = outputs.map(file => fs.statSync(file).mtimeMs); - await generate(); - assert.deepEqual(outputs.map(file => fs.statSync(file).mtimeMs), timestamps); - await generate(true); - for (const file of outputs) assert.notEqual(fs.statSync(file).mtimeMs, oldTime.getTime()); - for (const [index, file] of outputs.entries()) { - if (index % 2) fs.rmSync(file); - else fs.writeFileSync(file, "modified"); - } - await generate(); - assert.deepEqual(outputs.map(file => fs.readFileSync(file)), contents); -}); - -test("localization tracks source membership and package strings", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const extension = path.join(root, "packages/vscode-typescript"); - const source = path.join(extension, "src/cache-probe.ts"); - const packageStrings = path.join(extension, "package.nls.json"); - const bundle = path.join(extension, "l10n/bundle.l10n.json"); - const pseudoBundle = path.join(extension, "l10n/bundle.l10n.qps-ploc.json"); - const pseudoPackage = path.join(extension, "package.nls.qps-ploc.json"); - const originals = [packageStrings, bundle, pseudoBundle, pseudoPackage].map(file => [file, fs.readFileSync(file)] as const); - assert.equal(fs.existsSync(source), false); - context.after(() => { - fs.rmSync(source, { force: true }); - for (const [file, content] of originals) fs.writeFileSync(file, content); - }); - const generate = () => x("npx", ["hereby", "generate:extension-test"], { throwOnError: true, nodeOptions: { cwd: root } }); - for (const message of ["Codegen cache probe", "Updated codegen cache probe"]) { - fs.writeFileSync(source, `import * as vscode from "vscode";\nexport const message = vscode.l10n.t(${JSON.stringify(message)});\n`); - await generate(); - assert.ok(Object.hasOwn(JSON.parse(fs.readFileSync(bundle, "utf8")), message)); - assert.ok(Object.hasOwn(JSON.parse(fs.readFileSync(pseudoBundle, "utf8")), message)); - } - fs.rmSync(source); - await generate(); - assert.deepEqual(fs.readFileSync(bundle), originals[1][1]); - assert.deepEqual(fs.readFileSync(pseudoBundle), originals[2][1]); - const strings = JSON.parse(fs.readFileSync(packageStrings, "utf8")); - strings["cache.probe"] = "Package cache probe"; - fs.writeFileSync(packageStrings, JSON.stringify(strings)); - await generate(); - assert.ok(Object.hasOwn(JSON.parse(fs.readFileSync(pseudoPackage, "utf8")), "cache.probe")); -}); - -test("vendoring tracks package versions and supports force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const source = path.join(root, "node_modules/vscode-jsonrpc/lib/cache-probe.txt"); - const destination = path.join(root, "packages/typescript/vendor/vscode-jsonrpc/lib/cache-probe.txt"); - const sourceManifest = path.join(root, "node_modules/vscode-jsonrpc/package.json"); - const destinationManifest = path.join(root, "packages/typescript/vendor/vscode-jsonrpc/package.json"); - const originalSourceManifest = fs.readFileSync(sourceManifest); - const originalDestinationManifest = fs.readFileSync(destinationManifest); - assert.equal(fs.existsSync(source), false); - assert.equal(fs.existsSync(destination), false); - context.after(() => { - fs.rmSync(source, { force: true }); - fs.rmSync(destination, { force: true }); - fs.writeFileSync(sourceManifest, originalSourceManifest); - fs.writeFileSync(destinationManifest, originalDestinationManifest); - }); - const generate = (force = false) => x("npx", ["hereby", "generate:vendor", ...(force ? ["--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - fs.writeFileSync(source, "first"); - await generate(); - assert.equal(fs.existsSync(destination), false); - const manifest = JSON.parse(originalSourceManifest.toString()); - manifest.version += "-cache-probe"; - fs.writeFileSync(sourceManifest, JSON.stringify(manifest)); - await generate(); - assert.equal(fs.readFileSync(destination, "utf8"), "first"); - assert.equal(JSON.parse(fs.readFileSync(destinationManifest, "utf8")).version, manifest.version); - fs.writeFileSync(source, "changed"); - await generate(); - assert.equal(fs.readFileSync(destination, "utf8"), "first"); - await generate(true); - assert.equal(fs.readFileSync(destination, "utf8"), "changed"); - fs.rmSync(source); - await generate(); - assert.equal(fs.readFileSync(destination, "utf8"), "changed"); - fs.rmSync(destinationManifest); - await generate(); - assert.equal(fs.existsSync(destination), false); - assert.equal(JSON.parse(fs.readFileSync(destinationManifest, "utf8")).version, manifest.version); -}); - -test("bundled generation skips unchanged library outputs", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = () => x("go", ["-C", "./tsc", "generate", "./internal/bundled"], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const files = ["libs_generated.go", "embed_generated.go"].map(file => path.join(root, "tsc/internal/bundled", file)); - const timestamps = files.map(file => fs.statSync(file).mtimeMs); - assert.match((await generate()).stdout, /codegen outputs are already up to date/); - assert.deepEqual(files.map(file => fs.statSync(file).mtimeMs), timestamps); -}); - -test("bundled generation tracks libraries, validates inputs, and supports force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_bundled-probe-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true, maxRetries: 10 })); - const notice = path.join(directory, "CopyrightNotice.txt"); - const libs = path.join(directory, "libs"); - fs.mkdirSync(libs); - fs.writeFileSync(notice, "Fixture notice\n\n"); - const library = path.join(libs, "lib.first.d.ts"); - const content = "Fixture notice\n\n\ndeclare const first: string;\n"; - fs.writeFileSync(library, content); - const options = { - cwd: directory, - inputs: [path.join(root, "tsc/internal/bundled/generate.go"), "CopyrightNotice.txt", "libs/*"], - outputs: ["libs_generated.go", "embed_generated.go"], - commands: [["go", "run", path.join(root, "tsc/internal/bundled/generate.go")]], - }; - const generate = (force = false) => cache({ ...options, force }); - await generate(); - const files = ["libs_generated.go", "embed_generated.go"].map(file => path.join(directory, file)); - const originals = files.map(file => fs.readFileSync(file)); - assert.equal(await generate(), true); - const added = path.join(libs, "lib.second.d.ts"); - fs.writeFileSync(added, content); - assert.equal(await generate(), false); - for (const file of files) assert.match(fs.readFileSync(file, "utf8"), /lib\.second\.d\.ts/); - fs.rmSync(added); - assert.equal(await generate(), false); - for (const [index, file] of files.entries()) { - assert.deepEqual(fs.readFileSync(file), originals[index]); - fs.writeFileSync(file, "modified"); - assert.equal(await generate(), false); - assert.deepEqual(fs.readFileSync(file), originals[index]); - fs.rmSync(file, { recursive: true, maxRetries: 10 }); - assert.equal(await generate(), false); - assert.deepEqual(fs.readFileSync(file), originals[index]); - } - fs.writeFileSync(library, content.replace("string", "number")); - assert.equal(await generate(), false); - fs.writeFileSync(library, content.replaceAll("\n", "\r\n")); - await assert.rejects(generate()); - fs.writeFileSync(library, content); - assert.equal(await generate(), false); - fs.mkdirSync(path.join(libs, "unexpected")); - await assert.rejects(generate()); - fs.rmdirSync(path.join(libs, "unexpected")); - assert.equal(await generate(), false); - fs.writeFileSync(notice, "Changed fixture notice\n\n"); - await assert.rejects(generate()); - fs.writeFileSync(notice, "Fixture notice\n\n"); - assert.equal(await generate(), false); - assert.equal(await generate(true), false); - assert.equal(await generate(), true); -}); - -test("stringer generation tracks declared inputs, options, output, and force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_stringer-probe-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - const source = path.join(directory, "kind.go"); - const extra = path.join(directory, "extra.go"); - const output = path.join(directory, "kind_stringer_generated.go"); - fs.writeFileSync(source, "package probe\n\ntype Kind int\n\nconst KindFirst Kind = 0\n"); - const generate = (trimPrefix = false, force = false) => - cache({ - cwd: directory, - inputs: ["kind.go"], - outputs: ["kind_stringer_generated.go"], - env: { GOFILE: "kind.go" }, - force, - commands: [ - ["go", "tool", "golang.org/x/tools/cmd/stringer", "-type=Kind", "-output=kind_stringer_generated.go", ...(trimPrefix ? ["-trimprefix=Kind"] : [])], - ["dprint", "fmt", "kind_stringer_generated.go"], - ], - }); - await generate(); - const timestamp = fs.statSync(output).mtimeMs; - assert.equal(await generate(), true); - assert.equal(fs.statSync(output).mtimeMs, timestamp); - fs.writeFileSync(extra, "package probe\n\nconst Unrelated = 1\n"); - assert.equal(await generate(), true); - fs.appendFileSync(source, "\nconst KindSecond Kind = 1\n"); - assert.equal(await generate(), false); - assert.match(fs.readFileSync(output, "utf8"), /KindSecond/); - fs.rmSync(extra); - assert.equal(await generate(), true); - fs.writeFileSync(source, "package probe\n\ntype Kind int\n\nconst KindFirst Kind = 0\n"); - assert.equal(await generate(), false); - assert.doesNotMatch(fs.readFileSync(output, "utf8"), /KindSecond/); - assert.equal(await generate(true), false); - assert.match(fs.readFileSync(output, "utf8"), /_Kind_name = "First"/); - assert.equal(await generate(true), true); - fs.appendFileSync(output, "\n"); - assert.equal(await generate(true), false); - fs.rmSync(output); - assert.equal(await generate(true), false); - assert.equal(await generate(true, true), false); - assert.equal(await generate(true), true); - fs.appendFileSync(source, "\nconst KindInvalid Kind = missing\n"); - await assert.rejects(generate(true)); - fs.writeFileSync(source, "package probe\n\ntype Kind int\n\nconst KindFirst Kind = 0\n"); - assert.equal(await generate(true), false); - assert.equal(await generate(true), true); -}); - -test("moq generation tracks interface inputs, output, and force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_moq-probe-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - const source = path.join(directory, "client.go"); - const base = path.join(directory, "base.go"); - fs.mkdirSync(path.join(directory, "mock")); - const output = path.join(directory, "mock/mock_generated.go"); - fs.writeFileSync(base, "package probe\n\ntype Base interface { Read() string }\n"); - fs.writeFileSync(source, "package probe\n\ntype Client interface { Base }\n"); - const options = { - cwd: directory, - inputs: ["client.go", "base.go"], - outputs: ["mock/mock_generated.go"], - commands: [ - ["go", "tool", "github.com/matryer/moq", "-stub", "-fmt=goimports", "-pkg=mock", "-out=mock/mock_generated.go", ".", "Client"], - ["dprint", "fmt", "mock/mock_generated.go"], - ], - }; - const generate = (force = false) => cache({ ...options, force }); - await generate(); - const timestamp = fs.statSync(output).mtimeMs; - assert.equal(await generate(), true); - assert.equal(fs.statSync(output).mtimeMs, timestamp); - fs.writeFileSync(base, "package probe\n\ntype Base interface { Read() string; Close() error }\n"); - assert.equal(await generate(), false); - assert.match(fs.readFileSync(output, "utf8"), /CloseFunc/); - fs.rmSync(output); - assert.equal(await generate(), false); - assert.equal(await generate(true), false); - assert.equal(await generate(), true); -}); - -test("diagnostic generation tracks Go and locale outputs and supports force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_diagnostics-probe-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - const output = path.join(directory, "diagnostics_generated.go"); - const source = path.join(directory, "diagnosticMessages.generated.json"); - const localized = path.join(directory, "loc_generated.go"); - const locDirectory = path.join(directory, "loc"); - fs.mkdirSync(locDirectory); - const handbacks = fs.globSync("loc/*.generated.json", { cwd: path.join(root, "tsc/internal/diagnostics") }); - for (const handback of handbacks) { - fs.copyFileSync(path.join(root, "tsc/internal/diagnostics", handback), path.join(locDirectory, path.basename(handback))); - } - const project = path.join(directory, "tools/LocProject.json"); - fs.mkdirSync(path.dirname(project)); - const projectData = JSON.parse(fs.readFileSync(path.join(root, "tools/LocProject.json"), "utf8")); - projectData.Projects[0].LocItems = [projectData.Projects[0].LocItems[0]]; - projectData.Projects[0].LocItems[0].SourceFile = "diagnosticMessages.generated.json"; - fs.writeFileSync(project, JSON.stringify(projectData)); - const options = { - cwd: path.join(root, "tsc/internal/diagnostics"), - inputs: ["generate.go", "diagnosticMessages.json", project, "../{collections,json}/*.go", path.join(locDirectory, "*.generated.json")], - exclude: ["**/*_test.go"], - outputs: [output, source, localized, path.join(locDirectory, "*.json.gz")], - commands: [ - ["go", "run", "generate.go", "-diagnostics", output, "-loc", localized, "-locdir", locDirectory, "-locproject", project, "-locsource", source], - ["dprint", "fmt", output, localized], - ], - }; - const generate = (force = false) => cache({ ...options, force }); - await generate(); - const locales = fs.globSync("loc/*.json.gz", { cwd: directory }).map(file => path.join(directory, file)); - assert.ok(locales.length > 0); - const files = [output, source, localized, ...locales]; - const timestamps = files.map(file => fs.statSync(file).mtimeMs); - assert.equal(await generate(), true); - assert.deepEqual(files.map(file => fs.statSync(file).mtimeMs), timestamps); - fs.appendFileSync(project, "\n"); - assert.equal(await generate(), false); - fs.appendFileSync(path.join(locDirectory, path.basename(handbacks[0])), "\n"); - assert.equal(await generate(), false); - const archive = fs.readFileSync(locales[0]); - fs.rmSync(locales[0]); - assert.equal(await generate(), false); - assert.deepEqual(fs.readFileSync(locales[0]), archive); - fs.writeFileSync(locales[0], "modified"); - assert.equal(await generate(), false); - assert.deepEqual(fs.readFileSync(locales[0]), archive); - const unexpected = path.join(directory, "loc/unexpected.json.gz"); - fs.writeFileSync(unexpected, "unexpected"); - assert.equal(await generate(), false); - assert.equal(fs.existsSync(unexpected), false); - for (const locale of locales) fs.rmSync(locale); - assert.equal(await generate(), false); - assert.deepEqual(fs.readFileSync(locales[0]), archive); - fs.rmSync(localized); - assert.equal(await generate(), false); - assert.equal(await generate(true), false); - assert.equal(await generate(), true); -}); - -test("Unicode generation skips unchanged tables and formatting", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = () => x("go", ["-C", "./tsc", "generate", "./internal/stringutil"], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const files = ["js_case_generated.go", "identifier_parts_generated.go"].map(file => path.join(root, "tsc/internal/stringutil", file)); - const timestamps = files.map(file => fs.statSync(file).mtimeMs); - const current = await generate(); - assert.match(current.stdout, /Unicode tables are up to date\./); - assert.doesNotMatch(current.stdout, /Formatted/); - assert.deepEqual(files.map(file => fs.statSync(file).mtimeMs), timestamps); -}); - -test("Unicode generation repairs outputs and supports force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const directory = fs.mkdtempSync(path.join(root, "tsc/internal/_unicode-probe-")); - context.after(() => fs.rmSync(directory, { recursive: true, force: true })); - const command = [path.join(root, "tsc/internal/stringutil/_scripts/generate-unicode-data.mts"), "--outDir", directory]; - const generate = (...args: string[]) => x(process.execPath, [...command, ...args], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const files = ["js_case_generated.go", "identifier_parts_generated.go"].map(file => path.join(directory, file)); - const originals = files.map(file => fs.readFileSync(file)); - const timestamps = files.map(file => fs.statSync(file).mtimeMs); - assert.match((await generate()).stdout, /Unicode tables are up to date\./); - assert.deepEqual(files.map(file => fs.statSync(file).mtimeMs), timestamps); - for (const [index, file] of files.entries()) { - assert.deepEqual(originals[index], fs.readFileSync(path.join(root, "tsc/internal/stringutil", path.basename(file)))); - fs.writeFileSync(file, "modified"); - assert.match((await generate()).stdout, /Generated Unicode tables\./); - assert.deepEqual(fs.readFileSync(file), originals[index]); - fs.rmSync(file); - assert.match((await generate()).stdout, /Generated Unicode tables\./); - assert.deepEqual(fs.readFileSync(file), originals[index]); - } - const cacheFile = path.join(defaultCacheDirectory, createHash("sha256").update(files[0]).digest("hex") + ".json"); - context.after(() => fs.rmSync(cacheFile, { force: true })); - fs.rmSync(cacheFile); - assert.match((await generate()).stdout, /Generated Unicode tables\./); - assert.match((await generate("--force")).stdout, /Generated Unicode tables\./); - assert.match((await generate()).stdout, /Unicode tables are up to date\./); - const forced = await x(process.execPath, command, { - throwOnError: true, - nodeOptions: { cwd: root, env: { ...process.env, TSGO_HEREBY_FORCE: "1" } }, - }); - assert.match(forced.stdout, /Generated Unicode tables\./); - const overridden = await x(process.execPath, [...command, "--no-force"], { - throwOnError: true, - nodeOptions: { cwd: root, env: { ...process.env, TSGO_HEREBY_FORCE: "1" } }, - }); - assert.match(overridden.stdout, /Unicode tables are up to date\./); -}); - -test("enum generation skips unchanged outputs and Go verification", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = (force = false) => x("npx", ["hereby", "generate:enums", ...(force ? ["--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const files = [ - ...fs.globSync("packages/typescript/enums/*.{js,ts}", { cwd: root }), - "tsc/internal/api/enum_values_generated.go", - ].map(file => path.join(root, file)); - const timestamps = files.map(file => fs.statSync(file).mtimeMs); - const { stdout } = await generate(); - assert.match(stdout, /Enums are up to date\./); - assert.doesNotMatch(stdout, /Getting values from go/); - assert.deepEqual(files.map(file => fs.statSync(file).mtimeMs), timestamps); - const forced = await generate(true); - assert.match(forced.stdout, /All generated values match Go\./); - assert.match((await generate()).stdout, /Enums are up to date\./); - const verifier = path.join(root, "tsc/internal/api/enum_values_generated.go"); - const cacheFile = path.join(defaultCacheDirectory, createHash("sha256").update(verifier).digest("hex") + ".json"); - fs.rmSync(cacheFile); - const regenerated = await generate(); - assert.match(regenerated.stdout, /All generated values match Go\./); - assert.match((await generate()).stdout, /Enums are up to date\./); - - const astSchema = path.join(root, "tools/scripts/tsc/ast.json"); - const originalSchema = fs.readFileSync(astSchema, "utf8"); - const goKinds = path.join(root, "tsc/internal/ast/kind_generated.go"); - const goKindsTimestamp = fs.statSync(goKinds).mtimeMs; - try { - fs.writeFileSync(astSchema, originalSchema + "\n"); - assert.match((await generate()).stdout, /All generated values match Go\./); - assert.equal(fs.statSync(goKinds).mtimeMs, goKindsTimestamp); - assert.match((await generate()).stdout, /Enums are up to date\./); - } - finally { - fs.writeFileSync(astSchema, originalSchema); - await generate(); - } -}); - -test("AST generation forwards force to schema generators and the kind stringer", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = (force = false) => x("npx", ["hereby", "generate:ast", ...(force ? ["--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const output = path.join(root, "tsc/internal/ast/kind_stringer_generated.go"); - const oldTime = new Date("2000-01-01T00:00:00Z"); - fs.utimesSync(output, oldTime, oldTime); - const forced = await generate(true); - assert.match(forced.stdout, /Wrote .*encoder_generated\.go/); - assert.match(forced.stdout, /Wrote .*ast_generated\.go/); - assert.match(forced.stdout, /Generated .*ast\.generated\.ts/); - assert.notEqual(fs.statSync(output).mtimeMs, oldTime.getTime()); - const timestamp = fs.statSync(output).mtimeMs; - assert.doesNotMatch(forced.stdout, /\$ node .*tools\/scripts\/tsc\/generate\.ts/); - const current = await generate(); - assert.doesNotMatch(current.stdout, /(?:Wrote|Generated) /); - assert.match(current.stdout, /codegen outputs are already up to date/); - assert.equal(fs.statSync(output).mtimeMs, timestamp); -}); - -test("package generation forwards force to AST, encoder, and sync generators", async () => { - const root = path.resolve(import.meta.dirname, "../../.."); - const generate = (force = false) => x("npm", ["run", "-w", "@typescript/typescript", "generate", ...(force ? ["--", "--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const forced = await generate(true); - assert.match(forced.stdout, /Generated .*ast\.generated\.ts/); - assert.match(forced.stdout, /Wrote .*encoder_generated\.go/); - assert.match(forced.stdout, /Formatting\.\.\./); - const current = await generate(); - assert.doesNotMatch(current.stdout, /(?:Wrote|Generated) |Formatting\.\.\./); - assert.match(current.stdout, /Sync API is up to date\./); -}); - -test("API protocol generation caches formatted output and supports force", async context => { - const root = path.resolve(import.meta.dirname, "../../.."); - const output = path.join(root, "packages/typescript/src/api/proto.generated.ts"); - const compilerOptions = path.join(root, "packages/typescript/src/api/compilerOptions.generated.ts"); - const compilerOptionsTimestamp = fs.statSync(compilerOptions).mtimeMs; - const generate = (force = false) => x("npx", ["hereby", "generate:api", ...(force ? ["--force"] : [])], { throwOnError: true, nodeOptions: { cwd: root } }); - await generate(); - const timestamp = fs.statSync(output).mtimeMs; - const current = await generate(); - assert.match(current.stdout, /codegen outputs are already up to date/); - assert.doesNotMatch(current.stdout, /\$ node .*cache\.mts/); - assert.equal(fs.statSync(output).mtimeMs, timestamp); - const unrelated = path.join(root, "tsc/internal/parser/codegen_cache_probe.go"); - assert.equal(fs.existsSync(unrelated), false); - context.after(() => fs.rmSync(unrelated, { force: true })); - fs.writeFileSync(unrelated, "package parser\n"); - assert.match((await generate()).stdout, /codegen outputs are already up to date/); - fs.rmSync(unrelated); - const schema = path.join(root, "tsc/internal/api/requestfilesystem/codegen_cache_probe.go"); - assert.equal(fs.existsSync(schema), false); - const original = fs.readFileSync(output, "utf8"); - context.after(async () => { - if (fs.existsSync(schema)) { - fs.rmSync(schema); - await generate(true); - } - }); - fs.writeFileSync(schema, 'package requestfilesystem\n\nconst KindCodegenCacheProbe Kind = "codegen-cache-probe"\n'); - await generate(); - assert.match(fs.readFileSync(output, "utf8"), /"codegen-cache-probe"/); - fs.rmSync(schema); - await generate(); - assert.equal(fs.readFileSync(output, "utf8"), original); - const oldTime = new Date("2000-01-01T00:00:00Z"); - fs.utimesSync(output, oldTime, oldTime); - await generate(true); - assert.notEqual(fs.statSync(output).mtimeMs, oldTime.getTime()); - assert.equal(fs.statSync(compilerOptions).mtimeMs, compilerOptionsTimestamp, "Protocol generation must not regenerate compiler option definitions"); - assert.match((await generate()).stdout, /codegen outputs are already up to date/); - fs.utimesSync(output, oldTime, oldTime); - const nested = await x("go", ["-C", "./tsc", "generate", "./internal/api"], { - throwOnError: true, - nodeOptions: { cwd: root, env: { ...process.env, TSGO_HEREBY_FORCE: "1" } }, - }); - assert.notEqual(fs.statSync(output).mtimeMs, oldTime.getTime()); - assert.match((await generate()).stdout, /codegen outputs are already up to date/); -}); diff --git a/tools/scripts/gen/lspTypeScript.test.mts b/tools/scripts/gen/lspTypeScript.test.mts index 888120494c75a..b0d407cb03955 100644 --- a/tools/scripts/gen/lspTypeScript.test.mts +++ b/tools/scripts/gen/lspTypeScript.test.mts @@ -4,13 +4,17 @@ import fs from "node:fs"; import path from "node:path"; import { test } from "node:test"; import ts from "typescript"; -import { getTypeScriptModel } from "../lsp/generate.mts"; -import { getMiddlewareMethods } from "../lsp/generateTypeScript.mts"; +import { ensureModel } from "../lsp/fetchModel.mts"; import type { MetaModel, Type, } from "../lsp/metaModelSchema.mts"; -import { generateTypeScript } from "../lsp/typeScript.mts"; + +await ensureModel(); + +const { getTypeScriptModel } = await import("../lsp/generate.mts"); +const { getMiddlewareMethods } = await import("../lsp/generateTypeScript.mts"); +const { generateTypeScript } = await import("../lsp/typeScript.mts"); function fixture(): MetaModel { return { @@ -131,7 +135,7 @@ test("committed TypeScript protocol types are reproducible from the shared model const outputPath = path.join(root, "packages/typescript/src/vscode/protocol.generated.ts"); const methods = getMiddlewareMethods(fs.readFileSync(path.join(root, "packages/vscode-typescript/src/lspMiddleware.ts"), "utf8")); const output = generateTypeScript(getTypeScriptModel(), methods); - const formatted = execFileSync(path.join(root, "node_modules/.bin/dprint"), ["fmt", "--stdin", outputPath], { input: output, encoding: "utf8" }); + const formatted = execFileSync(process.execPath, [path.join(root, "node_modules/dprint/bin.cjs"), "fmt", "--stdin", outputPath], { input: output, encoding: "utf8" }); assert.equal(formatted, fs.readFileSync(outputPath, "utf8")); const file = ts.createSourceFile("generated.ts", output, ts.ScriptTarget.Latest, true); assert.ok(file.statements.every(statement => ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement))); diff --git a/tools/scripts/gen/utils.mts b/tools/scripts/gen/utils.mts index fc8e830103d62..bae735bc518a5 100644 --- a/tools/scripts/gen/utils.mts +++ b/tools/scripts/gen/utils.mts @@ -42,6 +42,16 @@ function beginFileFingerprintWrite(): () => void { }; } +export async function withFileFingerprintWrites(action: () => T | PromiseLike): Promise { + const finishWrite = beginFileFingerprintWrite(); + try { + return await action(); + } + finally { + finishWrite(); + } +} + export function getFileFingerprint(file: string, fresh = false): string { file = path.resolve(file); fresh ||= activeFingerprintWriters > 0; @@ -76,9 +86,8 @@ export function formatCommandArg(arg: string) { export async function run(command: string, args: readonly string[] = [], options: RunOptions = {}) { console.log("$ " + [command, ...args].map(formatCommandArg).join(" ")); - const finishWrite = beginFileFingerprintWrite(); - try { - return await x(command, args, { + return withFileFingerprintWrites(() => + x(command, args, { throwOnError: true, ...(options.signal ? { signal: options.signal } : {}), nodeOptions: { @@ -86,11 +95,8 @@ export async function run(command: string, args: readonly string[] = [], options env: options.env ? { ...process.env, ...options.env } : undefined, stdio: options.captureOutput ? "pipe" : "inherit", }, - }); - } - finally { - finishWrite(); - } + }) + ); } export function globInputs(patterns: string[], exclude: string[] = []): string[] { @@ -115,13 +121,7 @@ export function parseGeneratorArgs } export async function formatFiles(files: string[]): Promise { - const finishWrite = beginFileFingerprintWrite(); - try { - await x("dprint", ["fmt", ...files], { throwOnError: true, nodeOptions: { cwd: repoRoot, stdio: "inherit" } }); - } - finally { - finishWrite(); - } + await withFileFingerprintWrites(() => x("dprint", ["fmt", ...files], { throwOnError: true, nodeOptions: { cwd: repoRoot, stdio: "inherit" } })); } export function formatFilesSync(files: string[]): void { diff --git a/tools/scripts/lsp/fetchModel.mts b/tools/scripts/lsp/fetchModel.mts index e24a717d8ba9d..25a945a535cd1 100644 --- a/tools/scripts/lsp/fetchModel.mts +++ b/tools/scripts/lsp/fetchModel.mts @@ -5,15 +5,27 @@ import fs from "node:fs"; import path from "node:path"; import url from "node:url"; +import { GeneratedFile } from "../gen/generatedFile.mts"; const __filename = url.fileURLToPath(new URL(import.meta.url)); const __dirname = path.dirname(__filename); -const metaModelPath = path.join(__dirname, "metaModel.json"); -const metaModelSchemaPath = path.join(__dirname, "metaModelSchema.mts"); +export const outputs = ["metaModel.json", "metaModelSchema.mts"] as const; +const metaModelPath = path.join(__dirname, outputs[0]); +const metaModelSchemaPath = path.join(__dirname, outputs[1]); +const lockfilePath = path.resolve(__dirname, "../../../package-lock.json"); + +export async function ensureModel(force = false): Promise { + const files = outputs.map(output => new GeneratedFile(path.join(__dirname, output), [__filename, lockfilePath])); + if (!files.every(file => file.isCurrent(force))) { + for (const file of files) file.invalidate(); + await fetchModel(); + for (const file of files) file.markCurrent(); + } + return files; +} export default async function fetchModel() { - const lockfilePath = path.resolve(__dirname, "../../../package-lock.json"); const lockfile = JSON.parse(fs.readFileSync(lockfilePath, "utf-8")); const clientVersion: string = lockfile.packages["node_modules/vscode-languageclient"].version; diff --git a/tools/scripts/testdata/tinygo-overlay/src/internal/poll/fd_wasip1.go b/tools/scripts/testdata/tinygo-overlay/src/internal/poll/fd_wasip1.go new file mode 100644 index 0000000000000..8ccf4020f9e1d --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/internal/poll/fd_wasip1.go @@ -0,0 +1,575 @@ +//go:build wasip1 + +// Package poll is a minimal subset of upstream Go's internal/poll, scoped +// to what is needed to back a wasip1 net implementation on top of +// TinyGo's cooperative-scheduler netpoll integration. +// +// On wasip1 the cooperative scheduler integrates poll_oneoff with FD +// waiters (see runtime/netpoll_wasip1.go and syscall/syscall_libc_wasip1.go). +// This package wraps the syscall layer to: +// +// - own the O_NONBLOCK policy decision (set on Init for pollable FDs), +// unblocking the EAGAIN→park retry loop that syscall.Read already has; +// - provide a Go-shaped FD type that net.* can use without reaching +// into syscall directly; +// - thread per-FD read/write deadlines through a runtime helper that +// lets a time.AfterFunc callback wake the parked goroutine; +// - dispatch socket FDs through wasi sock_recv / sock_send / sock_accept +// / sock_shutdown so net.Conn/net.Listener (via upstream Go's +// net/file_wasip1.go) work end-to-end. +package poll + +import ( + "errors" + "internal/task" + "syscall" + "time" + "unsafe" +) + +// ErrFileClosing is returned when a Read or Write is started on a closed FD. +var ErrFileClosing = errors.New("use of closed file") + +// ErrNetClosing is returned for network operations on a closed FD. +var ErrNetClosing = errors.New("use of closed network connection") + +// ErrDeadlineExceeded is returned by Read/Write when a deadline expired. +// Matches the error returned by os.IsTimeout-style helpers. +var ErrDeadlineExceeded = errors.New("i/o timeout") + +// ErrNoDeadline is returned if SetDeadline is called on an FD whose +// underlying type does not support deadlines. +var ErrNoDeadline = errors.New("file type does not support deadline") + +// pollMode constants must mirror runtime/netpoll_wasip1.go's pollRead/ +// pollWrite values. +const ( + pollModeRead uint8 = 1 + pollModeWrite uint8 = 2 +) + +//go:linkname runtime_netpoll_addwait runtime.runtime_netpoll_addwait +func runtime_netpoll_addwait(fd uint32, mode uint8) uintptr + +//go:linkname runtime_netpoll_done runtime.runtime_netpoll_done +func runtime_netpoll_done(pd uintptr) + +//go:linkname runtime_netpoll_pdfired runtime.runtime_netpoll_pdfired +func runtime_netpoll_pdfired(pd uintptr) bool + +//go:linkname runtime_netpoll_wake runtime.runtime_netpoll_wake +func runtime_netpoll_wake(pd uintptr) + +//go:linkname fd_fdstat_get_type syscall.fd_fdstat_get_type +func fd_fdstat_get_type(fd int) (syscall.Filetype, error) + +// wasiIovec / wasiCiovec mirror wasi-snapshot-preview1's iovec / ciovec +// records: a buffer pointer plus a 32-bit length. Marshalled inline with +// each fd_read / fd_write / sock_recv / sock_send call. +type wasiIovec struct { + buf *byte + bufLen uint32 +} + +// fd_read / fd_write are bound directly here rather than going through +// wasi-libc so the deadline-aware Read/Write loop can observe EAGAIN +// cleanly. Outside package runtime, TinyGo's wasmimport binding +// restricts us to unsafe.Pointer for struct args and uint32 for the +// errno result; the wasi spec uses *iovec and uint16 respectively but +// the wire layout is identical. +// +//go:wasmimport wasi_snapshot_preview1 fd_read +func wasi_fd_read(fd int32, iovs unsafe.Pointer, iovsLen uint32, nread unsafe.Pointer) uint32 + +//go:wasmimport wasi_snapshot_preview1 fd_write +func wasi_fd_write(fd int32, iovs unsafe.Pointer, iovsLen uint32, nwritten unsafe.Pointer) uint32 + +// sock_recv / sock_send are the socket-data wasi syscalls used when the +// FD's Filetype indicates a socket. We bind them here for the same +// reason as fd_read / fd_write: the deadline-aware loop wants direct +// access to the EAGAIN signal without libc's translation layer. +// +//go:wasmimport wasi_snapshot_preview1 sock_recv +func wasi_sock_recv(fd int32, riData unsafe.Pointer, riDataLen uint32, riFlags uint32, roDatalen unsafe.Pointer, roFlags unsafe.Pointer) uint32 + +//go:wasmimport wasi_snapshot_preview1 sock_send +func wasi_sock_send(fd int32, siData unsafe.Pointer, siDataLen uint32, siFlags uint32, soDatalen unsafe.Pointer) uint32 + +// SysFile carries per-FD bookkeeping that upstream Go's poll.FD uses to +// share an underlying syscall FD between an os.File and a net.Conn (see +// Copy below). RefCountPtr / RefCount handle the shared-ownership case; +// Filetype caches the wasi filetype so socket-vs-file dispatch in Read / +// Write is a single integer compare on the hot path. +// +// wasip1 is single-threaded, so the refcount is a plain int — no atomics +// needed. Match upstream's field naming for source-level compatibility +// with code that constructs FDs via struct literal (e.g. upstream's +// net/fd_fake.go). +type SysFile struct { + RefCountPtr *int32 + RefCount int32 + Filetype uint32 +} + +// init lazily allocates the refcount the first time it's needed (i.e. +// the first Init or Copy on this FD). A zero SysFile starts at refcount +// 1 — the FD's sole owner is the caller. +func (s *SysFile) init() { + if s.RefCountPtr == nil { + s.RefCount = 1 + s.RefCountPtr = &s.RefCount + } +} + +// ref increments the shared refcount and returns a SysFile that points +// at the same counter. Used by FD.Copy. +func (s *SysFile) ref() SysFile { + s.init() + *s.RefCountPtr++ + return SysFile{RefCountPtr: s.RefCountPtr, Filetype: s.Filetype} +} + +// destroy decrements the refcount and reports whether the underlying +// syscall FD should now be closed (i.e. this was the last owner). +func (s *SysFile) destroy() bool { + if s.RefCountPtr == nil { + return true + } + *s.RefCountPtr-- + return *s.RefCountPtr <= 0 +} + +// FD is the wasip1 file/socket descriptor wrapped with the bookkeeping +// that net and os rely on. It owns the lifecycle of the underlying +// syscall FD (modulo the Copy / refcount handoff between os.File and +// net.Conn). +// +// The struct mirrors upstream Go's internal/poll.FD field naming +// (Sysfd, IsStream, ZeroReadIsEOF, SysFile) so that upstream's +// net/file_wasip1.go and net/fd_fake.go can construct one via struct +// literal without modification. +type FD struct { + Sysfd int + SysFile SysFile + IsStream bool + ZeroReadIsEOF bool + closed bool + + // Per-FD deadlines, zero means "no deadline". Subsequent Read/Write + // calls observe whatever the current value is at call time; an + // in-flight call uses the deadline it captured at its start. + rDeadline time.Time + wDeadline time.Time +} + +// Init readies the FD for use. When pollable is true (i.e. the FD might +// block — sockets, pipes, FIFOs), Init sets O_NONBLOCK so that +// Read/Write enter the EAGAIN→park retry loop instead of blocking the +// entire wasm module. +// +// Init also caches the wasi filetype so the Read/Write hot path can +// dispatch socket vs file with a single integer compare. +// +// The net argument is currently ignored but kept for parity with +// upstream Go. +func (fd *FD) Init(net string, pollable bool) error { + _ = net + fd.SysFile.init() + if ft, err := fd_fdstat_get_type(fd.Sysfd); err == nil { + fd.SysFile.Filetype = uint32(ft) + } + if !pollable { + return nil + } + flags, err := syscall.Fcntl(fd.Sysfd, syscall.F_GETFL, 0) + if err != nil { + return err + } + if flags&syscall.O_NONBLOCK != 0 { + return nil + } + if _, err := syscall.Fcntl(fd.Sysfd, syscall.F_SETFL, flags|syscall.O_NONBLOCK); err != nil { + return err + } + return nil +} + +// Copy returns a duplicate FD that shares the underlying Sysfd through +// the SysFile refcount. The original and the copy can independently +// call Close — only the last one actually issues the syscall. Used by +// upstream net/file_wasip1.go to hand a socket FD off from an os.File +// to a net.Listener / net.Conn. +func (fd *FD) Copy() FD { + return FD{ + Sysfd: fd.Sysfd, + SysFile: fd.SysFile.ref(), + IsStream: fd.IsStream, + ZeroReadIsEOF: fd.ZeroReadIsEOF, + } +} + +// Close marks the FD closed. The underlying syscall FD is only released +// when the refcount drops to zero — earlier Close calls (e.g. on the +// os.File side after a successful net.FileConn handoff) just decrement. +func (fd *FD) Close() error { + if fd.closed { + return ErrFileClosing + } + fd.closed = true + if !fd.SysFile.destroy() { + return nil + } + return syscall.Close(fd.Sysfd) +} + +// Exist reports whether fd points to an actual FD wrapper (non-nil). +// Callers in package os hold a *FD via a per-target alias and need to +// nil-check without depending on the concrete type being a pointer. +func (fd *FD) Exist() bool { return fd != nil } + +// CloseFunc is the hook upstream net's poll package exposes so tests +// can intercept Close. We just point at syscall.Close. +var CloseFunc func(int) error = syscall.Close + +// String is an internal string definition for methods/functions that +// shouldn't be used outside the stdlib. Upstream net references it +// from rawconn.go to mark methods as not-for-external-use. +type String string + +// RawControl / RawRead / RawWrite back syscall.RawConn's three callback +// methods. They invoke f with the underlying FD; the bool return of +// RawRead / RawWrite controls retry-on-EAGAIN, which we implement by +// parking the goroutine on the netpoll registry and retrying until f +// returns true (the same loop upstream uses). +func (fd *FD) RawControl(f func(uintptr)) error { + if fd.closed { + return ErrFileClosing + } + f(uintptr(fd.Sysfd)) + return nil +} + +func (fd *FD) RawRead(f func(uintptr) bool) error { + if fd.closed { + return ErrFileClosing + } + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + wait(fd.Sysfd, pollModeRead) + } +} + +func (fd *FD) RawWrite(f func(uintptr) bool) error { + if fd.closed { + return ErrFileClosing + } + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + wait(fd.Sysfd, pollModeWrite) + } +} + +// Shutdown calls wasi sock_shutdown. how is one of syscall.SHUT_RD, +// SHUT_WR, SHUT_RDWR. +func (fd *FD) Shutdown(how int) error { + return syscall.Shutdown(fd.Sysfd, how) +} + +// Accept loops over wasi sock_accept, parking the goroutine on EAGAIN +// (waiting for a new connection) through the netpoll registry. Returns +// (newfd, sockaddr=nil, errcall, error) — sockaddr is always nil because +// wasi sock_accept doesn't return one. +func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { + deadline := fd.rDeadline + for { + if !deadline.IsZero() && !time.Now().Before(deadline) { + return -1, nil, "accept", ErrDeadlineExceeded + } + nfd, _, err := syscall.Accept(fd.Sysfd) + if err == nil { + return nfd, nil, "", nil + } + if err == syscall.EINTR { + continue + } + if err != syscall.EAGAIN { + return -1, nil, "accept", err + } + if deadline.IsZero() { + wait(fd.Sysfd, pollModeRead) + } else { + if perr := fd.parkUntil(pollModeRead, deadline); perr != nil { + return -1, nil, "accept", perr + } + } + } +} + +// isSocket reports whether the cached Filetype is a stream or datagram +// socket. False if Init was never called (Filetype stays 0 = UNKNOWN). +func (fd *FD) isSocket() bool { + ft := syscall.Filetype(fd.SysFile.Filetype) + return ft == syscall.FILETYPE_SOCKET_STREAM || ft == syscall.FILETYPE_SOCKET_DGRAM +} + +// Read reads from the FD into p. Sockets dispatch to sock_recv (which +// honours fd.rDeadline directly); regular files/pipes go through +// syscall.Read for the no-deadline fast path or readWithDeadline when +// a deadline is set. +func (fd *FD) Read(p []byte) (int, error) { + if fd.closed { + return 0, ErrFileClosing + } + if len(p) == 0 { + return 0, nil + } + if fd.isSocket() { + return fd.sockRecv(p) + } + if fd.rDeadline.IsZero() { + return byteCount(syscall.Read(fd.Sysfd, p)) + } + return fd.readWithDeadline(p) +} + +// Write writes p to the FD. Sockets dispatch to sock_send. Regular +// files / pipes go through syscall.Write or writeWithDeadline. +func (fd *FD) Write(p []byte) (int, error) { + if fd.closed { + return 0, ErrFileClosing + } + if fd.isSocket() { + return fd.sockSend(p) + } + if fd.wDeadline.IsZero() { + return byteCount(syscall.Write(fd.Sysfd, p)) + } + return fd.writeWithDeadline(p) +} + +// Pread reads from the FD at the given offset. Always file semantics — +// sockets aren't seekable so this never goes through sock_recv. +func (fd *FD) Pread(p []byte, off int64) (int, error) { + if fd.closed { + return 0, ErrFileClosing + } + return byteCount(syscall.Pread(fd.Sysfd, p, off)) +} + +// Pwrite writes to the FD at the given offset. +func (fd *FD) Pwrite(p []byte, off int64) (int, error) { + if fd.closed { + return 0, ErrFileClosing + } + return byteCount(syscall.Pwrite(fd.Sysfd, p, off)) +} + +// SetDeadline sets both the read and write deadlines. +func (fd *FD) SetDeadline(t time.Time) error { + fd.rDeadline = t + fd.wDeadline = t + return nil +} + +// SetReadDeadline sets the deadline for future Read calls. A zero t +// clears the deadline. +func (fd *FD) SetReadDeadline(t time.Time) error { + fd.rDeadline = t + return nil +} + +// SetWriteDeadline sets the deadline for future Write calls. +func (fd *FD) SetWriteDeadline(t time.Time) error { + fd.wDeadline = t + return nil +} + +// readWithDeadline implements the EAGAIN→park retry loop with deadline +// cancellation for non-socket FDs. The deadline is captured at function +// entry; later SetReadDeadline calls don't affect this in-flight Read. +func (fd *FD) readWithDeadline(p []byte) (int, error) { + deadline := fd.rDeadline + iov := wasiIovec{buf: &p[0], bufLen: uint32(len(p))} + for { + if !time.Now().Before(deadline) { + return 0, ErrDeadlineExceeded + } + var n uint32 + errno := wasi_fd_read(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&n)) + switch errno { + case 0: + return int(n), nil + case wasiErrnoIntr: + continue + case wasiErrnoAgain: + if err := fd.parkUntil(pollModeRead, deadline); err != nil { + return 0, err + } + default: + return 0, syscall.Errno(errno) + } + } +} + +func (fd *FD) writeWithDeadline(p []byte) (int, error) { + deadline := fd.wDeadline + var nn int + for { + if !time.Now().Before(deadline) { + return nn, ErrDeadlineExceeded + } + if nn == len(p) { + return nn, nil + } + buf := p[nn:] + iov := wasiIovec{buf: &buf[0], bufLen: uint32(len(buf))} + var n uint32 + errno := wasi_fd_write(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&n)) + switch errno { + case 0: + nn += int(n) + case wasiErrnoIntr: + // retry + case wasiErrnoAgain: + if err := fd.parkUntil(pollModeWrite, deadline); err != nil { + return nn, err + } + default: + return nn, syscall.Errno(errno) + } + } +} + +// sockRecv is the socket sibling of readWithDeadline / syscall.Read. +// Always issues sock_recv, regardless of deadline state, so callers +// that don't want fd_read on a socket get a guaranteed sock_recv path. +func (fd *FD) sockRecv(p []byte) (int, error) { + deadline := fd.rDeadline + iov := wasiIovec{buf: &p[0], bufLen: uint32(len(p))} + for { + if !deadline.IsZero() && !time.Now().Before(deadline) { + return 0, ErrDeadlineExceeded + } + var n uint32 + var roFlags uint32 + errno := wasi_sock_recv(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, 0, unsafe.Pointer(&n), unsafe.Pointer(&roFlags)) + switch errno { + case 0: + return int(n), nil + case wasiErrnoIntr: + continue + case wasiErrnoAgain: + if deadline.IsZero() { + wait(fd.Sysfd, pollModeRead) + } else if err := fd.parkUntil(pollModeRead, deadline); err != nil { + return 0, err + } + default: + return 0, syscall.Errno(errno) + } + } +} + +func (fd *FD) sockSend(p []byte) (int, error) { + deadline := fd.wDeadline + var nn int + for { + if !deadline.IsZero() && !time.Now().Before(deadline) { + return nn, ErrDeadlineExceeded + } + if nn == len(p) { + return nn, nil + } + buf := p[nn:] + iov := wasiIovec{buf: &buf[0], bufLen: uint32(len(buf))} + var n uint32 + errno := wasi_sock_send(int32(fd.Sysfd), unsafe.Pointer(&iov), 1, 0, unsafe.Pointer(&n)) + switch errno { + case 0: + nn += int(n) + case wasiErrnoIntr: + // retry + case wasiErrnoAgain: + if deadline.IsZero() { + wait(fd.Sysfd, pollModeWrite) + } else if err := fd.parkUntil(pollModeWrite, deadline); err != nil { + return nn, err + } + default: + return nn, syscall.Errno(errno) + } + } +} + +// wasi snapshot preview1 errno values, kept here so the read/write loops +// can compare without dragging in the full syscall errno table for a +// switch on three constants. These match the wasi spec exactly; see also +// syscall_libc_wasi.go in package syscall. +const ( + wasiErrnoAgain uint32 = 6 + wasiErrnoIntr uint32 = 27 +) + +// wait parks the current goroutine until the FD becomes ready in the +// given direction. No deadline; mirrors the helper of the same name in +// package syscall (intentionally duplicated rather than linknamed +// across the package boundary — see project memory on shim avoidance). +func wait(fd int, mode uint8) { + if task.OnSystemStack() { + task.Pause() + return + } + pd := runtime_netpoll_addwait(uint32(fd), mode) + task.Pause() + runtime_netpoll_done(pd) +} + +// parkUntil parks the current goroutine on (fd, mode) with a deadline. +// Returns nil if the FD became ready or the timer fired (caller's loop +// re-checks the deadline at top); ErrDeadlineExceeded if the deadline +// was already in the past. +// +// Race handling: the deadline timer's callback and pollIO's event walk +// can both target the same pollDesc. The pd.fired flag guards against +// double-pushing the task to the run queue; whichever arrives second +// is a no-op. +func (fd *FD) parkUntil(mode uint8, deadline time.Time) error { + d := time.Until(deadline) + if d <= 0 { + return ErrDeadlineExceeded + } + if task.OnSystemStack() { + task.Pause() + return nil + } + pd := runtime_netpoll_addwait(uint32(fd.Sysfd), mode) + timer := time.AfterFunc(d, func() { + runtime_netpoll_wake(pd) + }) + task.Pause() + timer.Stop() + runtime_netpoll_done(pd) + return nil +} + +// WaitWrite waits until data can be written to fd. +func (fd *FD) WaitWrite() error { + if fd.wDeadline.IsZero() { + wait(fd.Sysfd, pollModeWrite) + return nil + } + return fd.parkUntil(pollModeWrite, fd.wDeadline) +} + +// byteCount turns the -1 a failed read or write returns into 0, as Go's +// internal/poll does in fd_unix.go. +func byteCount(n int, err error) (int, error) { + if n < 0 { + n = 0 + } + return n, err +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/runtime/arch_tinygowasm.go b/tools/scripts/testdata/tinygo-overlay/src/runtime/arch_tinygowasm.go new file mode 100644 index 0000000000000..608911830323a --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/runtime/arch_tinygowasm.go @@ -0,0 +1,107 @@ +//go:build tinygo.wasm + +package runtime + +import ( + "unsafe" +) + +const GOARCH = "wasm" + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 32 + +// zeroSizedAlloc a sentinel that gets returned when allocating 0 bytes. +// Using this instead of a constant value since I can't easily find a memory +// location that is definitely not going to end up as a valid pointer. +var zeroSizedAlloc uint8 + +var zeroSizeAllocPtr = &zeroSizedAlloc + +const deferExtraRegs = 0 + +const callInstSize = 1 // unknown and irrelevant (llvm.returnaddress doesn't work), so make something up + +//go:extern __heap_base +var heapStartSymbol [0]byte + +//go:extern __global_base +var globalsStartSymbol [0]byte + +const ( + // wasmMemoryIndex is always zero until the multi-memory feature is used. + // + // See https://github.com/WebAssembly/multi-memory + wasmMemoryIndex = 0 + + // wasmPageSize is the size of a page in WebAssembly's 32-bit memory. This + // is also its only unit of change. + // + // See https://www.w3.org/TR/wasm-core-1/#page-size + wasmPageSize = 64 * 1024 +) + +// wasm_memory_size invokes the "memory.size" instruction, which returns the +// current size to the memory at the given index (always wasmMemoryIndex), in +// pages. +// +//export llvm.wasm.memory.size.i32 +func wasm_memory_size(index int32) int32 + +// wasm_memory_grow invokes the "memory.grow" instruction, which attempts to +// increase the size of the memory at the given index (always wasmMemoryIndex), +// by the delta (in pages). This returns the previous size on success of -1 on +// failure. +// +//export llvm.wasm.memory.grow.i32 +func wasm_memory_grow(index int32, delta int32) int32 + +var ( + // heapStart is the current memory offset which starts the heap. The heap + // extends from this offset until heapEnd (exclusive). + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + + // heapEnd is the current memory length in bytes. + heapEnd = uintptr(wasm_memory_size(wasmMemoryIndex) * wasmPageSize) + + globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) + globalsEnd = uintptr(unsafe.Pointer(&heapStartSymbol)) + + stackTop = uintptr(unsafe.Pointer(&globalsStartSymbol)) +) + +func align(ptr uintptr) uintptr { + // Align to 16, which is the alignment of max_align_t: + // https://godbolt.org/z/dYqTsWrGq + const heapAlign = 16 + return (ptr + heapAlign - 1) &^ (heapAlign - 1) +} + +//export tinygo_getCurrentStackPointer +func getCurrentStackPointer() uintptr + +// savedStackPointer is used to save the system stack pointer while in a goroutine. +var savedStackPointer uintptr + +// saveStackPointer is called by internal/task.state.Resume() to save the stack pointer before entering a goroutine from the system stack. +func saveStackPointer() { + savedStackPointer = getCurrentStackPointer() +} + +// growHeap tries to grow the heap size. It returns true if it succeeds, false +// otherwise. +func growHeap() bool { + // Grow memory by up to 64 MiB at a time and back off if the host caps memory. + // Cap total pages at 65535 so uintptr(pages) * wasmPageSize does not wrap to 0. + const maxPages = 65535 + memorySize := wasm_memory_size(wasmMemoryIndex) + growPages := min(memorySize, 1024, maxPages-memorySize) + for growPages > 0 { + if wasm_memory_grow(wasmMemoryIndex, growPages) != -1 { + setHeapEnd(uintptr(wasm_memory_size(wasmMemoryIndex)) * wasmPageSize) + return true + } + growPages /= 2 + } + return false +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/runtime/gc_leaking.go b/tools/scripts/testdata/tinygo-overlay/src/runtime/gc_leaking.go new file mode 100644 index 0000000000000..98e3458fb9527 --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/runtime/gc_leaking.go @@ -0,0 +1,243 @@ +//go:build gc.leaking + +package runtime + +// This GC implementation is the simplest useful memory allocator possible: it +// only allocates memory and never frees it. For some constrained systems, it +// may be the only memory allocator possible. + +import ( + "internal/task" + "sync/atomic" + "unsafe" +) + +const needsStaticHeap = true + +var gcScanState atomic.Uint32 + +func scanCurrentStack() {} + +// Ever-incrementing pointer: no memory is freed. +var ( + heapptr uintptr + small4Ptr uintptr + small4End uintptr + heapWatermark uintptr + liveAllocWatermark uint64 + heapPoolEpoch uint32 = 1 +) + +// Total amount allocated for runtime.MemStats +var ( + gcTotalAlloc uint64 + gcLiveAlloc uint64 +) + +// Total number of calls to alloc() +var ( + gcMallocs uint64 + gcFrees uint64 +) + +// Heap lock for parallel goroutines. No-op when single threaded. +var gcLock task.PMutex + +var ( + freeLists [257]unsafe.Pointer + largeFreePtr [256]unsafe.Pointer + largeFreeSize [256]uintptr +) + +//go:noinline +func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { + if size == 0 { + return unsafe.Pointer(zeroSizeAllocPtr) + } + if size <= 4 { + if small4Ptr == small4End { + chunk := uintptr(alloc(256, nil)) + small4Ptr = chunk + small4End = chunk + 256 + } else { + gcMallocs++ + } + addr := small4Ptr + small4Ptr += 4 + return unsafe.Pointer(addr) + } + size = align(size) + if size <= 2048 { + idx := size >> 3 + if p := freeLists[idx]; p != nil { + freeLists[idx] = *(*unsafe.Pointer)(p) + memzero(p, size) + gcTotalAlloc += uint64(size) + gcLiveAlloc += uint64(size) + gcMallocs++ + return p + } + } else { + for i := range 256 { + if largeFreeSize[i] == size { + p := largeFreePtr[i] + largeFreePtr[i] = nil + largeFreeSize[i] = 0 + memzero(p, size) + gcTotalAlloc += uint64(size) + gcLiveAlloc += uint64(size) + gcMallocs++ + return p + } + } + } + gcLock.Lock() + addr := heapptr + gcTotalAlloc += uint64(size) + gcLiveAlloc += uint64(size) + gcMallocs++ + heapptr += size + if heapptr < addr { + runtimePanic(errOutOfMemory) + } + for heapptr > heapEnd { + if growHeap() { + continue + } + runtimeFatal("out of memory") + } + gcLock.Unlock() + + pointer := unsafe.Pointer(addr) + if GOOS != "wasip1" && GOARCH != "wasm" { + zero_new_alloc(pointer, size) + } + return pointer +} + +func freeSized(ptr unsafe.Pointer, size uintptr) { + if ptr == nil || size < 8 { + return + } + size = align(size) + if size <= 2048 { + idx := size >> 3 + *(*unsafe.Pointer)(ptr) = freeLists[idx] + freeLists[idx] = ptr + gcFrees++ + gcLiveAlloc -= uint64(size) + } else { + for i := range 256 { + if largeFreeSize[i] == 0 { + largeFreePtr[i] = ptr + largeFreeSize[i] = size + gcFrees++ + gcLiveAlloc -= uint64(size) + return + } + } + } +} + +func free(ptr unsafe.Pointer) { +} + +//go:noinline +func freeTaskStack(ptr uintptr) { +} + +func markRoots(start, end uintptr) { + runtimeFatal("unreachable: markRoots") +} + +// ReadMemStats populates m with memory statistics. +// +// The returned memory statistics are up to date as of the +// call to ReadMemStats. This would not do GC implicitly for you. +func ReadMemStats(m *MemStats) { + gcLock.Lock() + + m.HeapIdle = 0 + m.HeapInuse = gcLiveAlloc + m.HeapReleased = 0 // always 0, we don't currently release memory back to the OS. + + m.HeapSys = m.HeapInuse + m.HeapIdle + m.GCSys = 0 + m.TotalAlloc = gcTotalAlloc + m.Mallocs = gcMallocs + m.Frees = gcFrees + m.Sys = uint64(heapEnd - heapStart) + m.HeapAlloc = gcLiveAlloc + m.HeapObjects = gcMallocs - gcFrees + m.Alloc = m.HeapAlloc + m.NumGC = 0 // this GC never collects, so no cycle ever completes + + gcLock.Unlock() +} + +func mallocs() uint64 { + gcLock.Lock() + mallocs := gcMallocs + gcLock.Unlock() + return mallocs +} + +func GC() { + // No-op. +} + +func SetFinalizer(obj interface{}, finalizer interface{}) { + // No-op. +} + +func initHeap() { + heapptr = heapStart +} +// setHeapEnd sets a new (larger) heapEnd pointer. +func setHeapEnd(newHeapEnd uintptr) { + // This "heap" is so simple that simply assigning a new value is good + // enough. + heapEnd = newHeapEnd +} + +//go:linkname PoolEpoch runtime.PoolEpoch +func PoolEpoch() uint32 { + return heapPoolEpoch +} + +//go:linkname MarkHeap runtime.MarkHeap +func MarkHeap() { + gcLock.Lock() + small4Ptr = 0 + small4End = 0 + freeLists = [257]unsafe.Pointer{} + largeFreePtr = [256]unsafe.Pointer{} + largeFreeSize = [256]uintptr{} + heapWatermark = heapptr + liveAllocWatermark = gcLiveAlloc + if heapPoolEpoch == 1 { + heapPoolEpoch = 2 + } else { + heapPoolEpoch++ + } + gcLock.Unlock() +} + +//go:linkname ResetHeap runtime.ResetHeap +func ResetHeap() { + gcLock.Lock() + if heapWatermark != 0 && heapptr > heapWatermark { + memzero(unsafe.Pointer(heapWatermark), heapptr-heapWatermark) + heapptr = heapWatermark + small4Ptr = 0 + small4End = 0 + gcLiveAlloc = liveAllocWatermark + freeLists = [257]unsafe.Pointer{} + largeFreePtr = [256]unsafe.Pointer{} + largeFreeSize = [256]uintptr{} + } + if heapPoolEpoch >= 2 { + heapPoolEpoch++ + } + gcLock.Unlock() +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/runtime/hashmap.go b/tools/scripts/testdata/tinygo-overlay/src/runtime/hashmap.go new file mode 100644 index 0000000000000..79f13369d0da1 --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/runtime/hashmap.go @@ -0,0 +1,892 @@ +package runtime + +// This is a hashmap implementation for the map[T]T type. +// It is very roughly based on the implementation of the Go hashmap: +// +// https://golang.org/src/runtime/map.go + +import ( + "internal/reflectlite" + "tinygo" + "unsafe" +) + +// The underlying hashmap structure for Go. +type hashmap struct { + buckets unsafe.Pointer // pointer to array of buckets + typeInfo *hashmapTypeInfo + seed uintptr + count uintptr + keySize uintptr + valueSize uintptr + keySlotSize uintptr // == keySize, or sizeof(ptr) if indirect + valueSlotSize uintptr // == valueSize, or sizeof(ptr) if indirect + bucketBits uint8 + flags uint8 + keyEqual func(x, y unsafe.Pointer, n uintptr) bool + keyHash func(key unsafe.Pointer, size, seed uintptr) uint32 +} + +type hashmapTypeInfo struct { + keyLayout unsafe.Pointer + valueLayout unsafe.Pointer + bucketLayout unsafe.Pointer +} + +//go:inline +func hashmapType(m *hashmap) *hashmapTypeInfo { + return m.typeInfo +} + +const ( + hashmapMaxKeySize = 128 + hashmapMaxValueSize = 128 + + hashmapFlagIndirectKey = 1 << 0 + hashmapFlagIndirectValue = 1 << 1 +) + +// A hashmap bucket. A bucket is a container of 8 key/value pairs: first the +// following two entries, then the 8 keys, then the 8 values. This somewhat odd +// ordering is to make sure the keys and values are well aligned when one of +// them is smaller than the system word size. +type hashmapBucket struct { + tophash [8]uint8 + next *hashmapBucket // next bucket (if there are more than 8 in a chain) + // Followed by the actual keys, and then the actual values. These are + // allocated but as they're of variable size they can't be shown here. +} + +// hashmapBucketHeaderSize is the offset in bytes from the start of a bucket to +// the first key, aligned to 8 bytes. This ensures that keys requiring 8-byte +// alignment (float64, complex128, uint64 on strict-alignment architectures +// like MIPS) are properly aligned in the bucket. +const hashmapBucketHeaderSize = (unsafe.Sizeof(hashmapBucket{}) + 7) &^ 7 + +// hashmapKeySlotSize returns the size of a key slot in the bucket. For indirect +// keys, this is the pointer size; otherwise the actual key size. +// +//go:inline +func hashmapKeySlotSize(m *hashmap) uintptr { + return m.keySlotSize +} + +// hashmapValueSlotSize returns the size of a value slot in the bucket. +// +//go:inline +func hashmapValueSlotSize(m *hashmap) uintptr { + return m.valueSlotSize +} + +// hashmapSlotKeyData returns a pointer to the actual key data for a given slot. +// For indirect keys, the slot contains a pointer that must be dereferenced. +// +//go:inline +func hashmapSlotKeyData(m *hashmap, slotKey unsafe.Pointer) unsafe.Pointer { + if m.flags&hashmapFlagIndirectKey != 0 { + return *(*unsafe.Pointer)(slotKey) + } + return slotKey +} + +// hashmapSlotValueData returns a pointer to the actual value data for a given slot. +// +//go:inline +func hashmapSlotValueData(m *hashmap, slotValue unsafe.Pointer) unsafe.Pointer { + if m.flags&hashmapFlagIndirectValue != 0 { + return *(*unsafe.Pointer)(slotValue) + } + return slotValue +} + +type hashmapIterator struct { + buckets unsafe.Pointer // pointer to array of hashapBuckets + numBuckets uintptr // length of buckets array + bucketNumber uintptr // current index into buckets array + startBucket uintptr // starting location for iterator + bucket *hashmapBucket // current bucket in chain + bucketIndex uint8 // current index into bucket + startIndex uint8 // starting bucket index for iterator + wrapped bool // true if the iterator has wrapped +} + +func hashmapNewIterator() unsafe.Pointer { + return unsafe.Pointer(new(hashmapIterator)) +} + +func hashmapResetIterator(it unsafe.Pointer) unsafe.Pointer { + if it == nil { + return hashmapNewIterator() + } + *(*hashmapIterator)(it) = hashmapIterator{} + return it +} + +// Get the topmost 8 bits of the hash, without using a special value (like 0). +func hashmapTopHash(hash uint32) uint8 { + tophash := uint8(hash >> 24) + if tophash < 1 { + // 0 means empty slot, so make it bigger. + tophash += 1 + } + return tophash +} + +// Create a new hashmap with the given keySize and valueSize. +func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, typeInfo unsafe.Pointer, alg uint8) *hashmap { + bucketBits := uint8(0) + if sizeHint > 8 { + for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) { + bucketBits++ + } + } + + var flags uint8 + keySlotSize := keySize + if keySize > hashmapMaxKeySize { + flags |= hashmapFlagIndirectKey + keySlotSize = unsafe.Sizeof(unsafe.Pointer(nil)) + } + valueSlotSize := valueSize + if valueSize > hashmapMaxValueSize { + flags |= hashmapFlagIndirectValue + valueSlotSize = unsafe.Sizeof(unsafe.Pointer(nil)) + } + + bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8 + buckets := alloc(bucketBufSize*(1< If the map or slice is nil, clear is a no-op. + return + } + + rem := m.count + m.count = 0 + numBuckets := uintptr(1) << m.bucketBits + for i := range numBuckets { + bucket := hashmapBucketAddr(m, m.buckets, i) + for bucket != nil { + if *(*uint64)(unsafe.Pointer(&bucket.tophash)) != 0 { + for j := range 8 { + if bucket.tophash[j] != 0 { + rem-- + } + } + bucket.tophash = [8]uint8{} + if rem == 0 { + return + } + } + bucket = bucket.next + } + } +} + +func hashmapKeyEqualAlg(alg tinygo.HashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool { + switch alg { + case tinygo.HashmapAlgorithmBinary: + return memequal + case tinygo.HashmapAlgorithmString: + return hashmapStringEqual + default: + // compiler bug :( + return nil + } +} + +func hashmapKeyHashAlg(alg tinygo.HashmapAlgorithm) func(key unsafe.Pointer, n, seed uintptr) uint32 { + switch alg { + case tinygo.HashmapAlgorithmBinary: + return hash32 + case tinygo.HashmapAlgorithmString: + return hashmapStringPtrHash + default: + // compiler bug :( + return nil + } +} + +func hashmapHasSpaceToGrow(bucketBits uint8) bool { + // Over this limit, we're likely to overflow uintptrs during calculations + // or numbers of hash elements. Don't allow any more growth. + // With 29 bits, this is 2^32 elements anyway. + return bucketBits <= uint8((unsafe.Sizeof(uintptr(0))*8)-3) +} + +func hashmapOverLoadFactor(n uintptr, bucketBits uint8) bool { + // "maximum" number of elements is 0.75 * buckets * elements per bucket + // to avoid overflow, this is calculated as + // max = 3 * (1/4 * buckets * elements per bucket) + // = 3 * (buckets * (elements per bucket)/4) + // = 3 * (buckets * (8/4) + // = 3 * (buckets * 2) + // = 6 * buckets + max := (uintptr(6) << bucketBits) + return n > max +} + +// Return the number of entries in this hashmap, called from the len builtin. +// A nil hashmap is defined as having length 0. +// +//go:inline +func hashmapLen(m *hashmap) int { + if m == nil { + return 0 + } + return int(m.count) +} + +//go:inline +func hashmapBucketSize(m *hashmap) uintptr { + return hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*8 +} + +//go:inline +func hashmapBucketAddr(m *hashmap, buckets unsafe.Pointer, n uintptr) *hashmapBucket { + bucketSize := hashmapBucketSize(m) + bucket := (*hashmapBucket)(unsafe.Add(buckets, bucketSize*n)) + return bucket +} + +//go:inline +func hashmapBucketAddrForHash(m *hashmap, hash uint32) *hashmapBucket { + numBuckets := uintptr(1) << m.bucketBits + bucketNumber := (uintptr(hash) & (numBuckets - 1)) + return hashmapBucketAddr(m, m.buckets, bucketNumber) +} + +//go:inline +func hashmapSlotKey(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer { + slotKeyOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*uintptr(slot) + return unsafe.Add(unsafe.Pointer(bucket), slotKeyOffset) +} + +//go:inline +func hashmapSlotValue(m *hashmap, bucket *hashmapBucket, slot uint8) unsafe.Pointer { + slotValueOffset := hashmapBucketHeaderSize + hashmapKeySlotSize(m)*8 + hashmapValueSlotSize(m)*uintptr(slot) + return unsafe.Add(unsafe.Pointer(bucket), slotValueOffset) +} + +// Set a specified key to a given value. Grow the map if necessary. +// +//go:nobounds +func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) { + if hashmapHasSpaceToGrow(m.bucketBits) && hashmapOverLoadFactor(m.count, m.bucketBits) { + hashmapGrow(m) + // seed changed when we grew; rehash key with new seed + hash = m.keyHash(key, m.keySize, m.seed) + } + + tophash := hashmapTopHash(hash) + bucket := hashmapBucketAddrForHash(m, hash) + var lastBucket *hashmapBucket + + // See whether the key already exists somewhere. + var emptySlotKey unsafe.Pointer + var emptySlotValue unsafe.Pointer + var emptySlotTophash *byte + for bucket != nil { + for i := uint8(0); i < 8; i++ { + slotKey := hashmapSlotKey(m, bucket, i) + slotValue := hashmapSlotValue(m, bucket, i) + if bucket.tophash[i] == 0 && emptySlotKey == nil { + // Found an empty slot, store it for if we couldn't find an + // existing slot. + emptySlotKey = slotKey + emptySlotValue = slotValue + emptySlotTophash = &bucket.tophash[i] + } + if bucket.tophash[i] == tophash { + // Could be an existing key that's the same. + if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) { + // found same key, replace the value + hashmapStoreValue(m, slotValue, value) + return + } + } + } + lastBucket = bucket + bucket = bucket.next + } + if emptySlotKey == nil { + // Add a new bucket to the bucket chain. + // TODO: rebalance if necessary to avoid O(n) insert and lookup time. + lastBucket.next = (*hashmapBucket)(hashmapInsertIntoNewBucket(m, key, value, tophash)) + return + } + m.count++ + hashmapStoreKey(m, emptySlotKey, key) + hashmapStoreValue(m, emptySlotValue, value) + *emptySlotTophash = tophash +} + +// hashmapStoreKey stores a key into a bucket slot, allocating backing storage +// if the key is indirect (first insert) or copying into it (shouldn't happen +// for keys, but handles it correctly). +// +//go:inline +func hashmapStoreKey(m *hashmap, slotKey, key unsafe.Pointer) { + if m.flags&hashmapFlagIndirectKey != 0 { + p := alloc(m.keySize, hashmapType(m).keyLayout) + memcpy(p, key, m.keySize) + *(*unsafe.Pointer)(slotKey) = p + } else { + memcpy(slotKey, key, m.keySize) + } +} + +// hashmapStoreValue stores a value into a bucket slot. For indirect values, +// it allocates backing storage on first insert or copies into the existing +// backing on overwrite. +// +//go:inline +func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) { + if m.flags&hashmapFlagIndirectValue != 0 { + p := *(*unsafe.Pointer)(slotValue) + if p == nil { + // First insert: allocate backing storage. + p = alloc(m.valueSize, hashmapType(m).valueLayout) + *(*unsafe.Pointer)(slotValue) = p + } + memcpy(p, value, m.valueSize) + } else { + memcpy(slotValue, value, m.valueSize) + } +} + +// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and +// value into the bucket, and returns a pointer to this bucket. +func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket { + bucketBufSize := hashmapBucketSize(m) + bucketBuf := alloc(bucketBufSize, hashmapType(m).bucketLayout) + bucket := (*hashmapBucket)(bucketBuf) + + // Insert into the first slot, which is empty as it has just been allocated. + slotKey := hashmapSlotKey(m, bucket, 0) + slotValue := hashmapSlotValue(m, bucket, 0) + m.count++ + hashmapStoreKey(m, slotKey, key) + hashmapStoreValue(m, slotValue, value) + bucket.tophash[0] = tophash + return bucket +} + +func hashmapGrow(m *hashmap) { + oldBuckets := m.buckets + oldNumBuckets := uintptr(1) << m.bucketBits + bucketBufSize := hashmapBucketSize(m) + + n := *m + n.count = 0 + n.bucketBits = m.bucketBits + 1 + numBuckets := uintptr(1) << n.bucketBits + n.buckets = alloc(bucketBufSize*numBuckets, hashmapType(m).bucketLayout) + + for i := range oldNumBuckets { + first := hashmapBucketAddr(m, oldBuckets, i) + b := first + for b != nil { + for j := range uint8(8) { + if b.tophash[j] != 0 { + slotKey := hashmapSlotKey(m, b, j) + slotValue := hashmapSlotValue(m, b, j) + keyPtr := hashmapSlotKeyData(m, slotKey) + valPtr := hashmapSlotValueData(m, slotValue) + h := n.keyHash(keyPtr, uintptr(n.keySize), n.seed) + hashmapSet(&n, keyPtr, valPtr, h) + } + } + next := b.next + if b != first { + freeSized(unsafe.Pointer(b), bucketBufSize) + } + b = next + } + } + freeSized(oldBuckets, bucketBufSize*oldNumBuckets) + *m = n +} + +//go:linkname hashmapClone maps.clone +func hashmapClone(intf _interface) _interface { + typ, val := decomposeInterface(intf) + m := (*hashmap)(val) + n := hashmapCopy(m, m.bucketBits) + return composeInterface(typ, unsafe.Pointer(&n)) +} + +func hashmapCopy(m *hashmap, sizeBits uint8) hashmap { + // clone map as empty + n := *m + n.count = 0 + n.seed = uintptr(fastrand()) + + n.bucketBits = sizeBits + numBuckets := uintptr(1) << n.bucketBits + bucketBufSize := hashmapBucketSize(m) + n.buckets = alloc(bucketBufSize*numBuckets, hashmapType(m).bucketLayout) + + // use a hashmap iterator to go through the old map + var it hashmapIterator + + key := alloc(m.keySize, hashmapType(m).keyLayout) + value := alloc(m.valueSize, hashmapType(m).valueLayout) + + for hashmapNext(m, &it, key, value) { + h := n.keyHash(key, uintptr(n.keySize), n.seed) + hashmapSet(&n, key, value, h) + } + + return n +} + +// Get the value of a specified key, or zero the value if not found. +// +//go:nobounds +func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool { + if m == nil || m.count == 0 { + // Getting a value out of a nil map is valid. From the spec: + // > if the map is nil or does not contain such an entry, a[x] is the + // > zero value for the element type of M + memzero(value, uintptr(valueSize)) + return false + } + + tophash := hashmapTopHash(hash) + bucket := hashmapBucketAddrForHash(m, hash) + + // Try to find the key. + for bucket != nil { + for i := uint8(0); i < 8; i++ { + slotKey := hashmapSlotKey(m, bucket, i) + if bucket.tophash[i] == tophash { + // This could be the key we're looking for. + if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) { + // Found the key, copy it. + slotValue := hashmapSlotValue(m, bucket, i) + memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize) + return true + } + } + } + bucket = bucket.next + } + + // Did not find the key. + memzero(value, m.valueSize) + return false +} + +// Delete a given key from the map. No-op when the key does not exist in the +// map. +// +//go:nobounds +func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) { + if m == nil || m.count == 0 { + // The delete builtin is defined even when the map is nil. From the spec: + // > If the map m is nil or the element m[k] does not exist, delete is a + // > no-op. + return + } + + tophash := hashmapTopHash(hash) + bucket := hashmapBucketAddrForHash(m, hash) + + // Try to find the key. + for bucket != nil { + for i := uint8(0); i < 8; i++ { + slotKey := hashmapSlotKey(m, bucket, i) + if bucket.tophash[i] == tophash { + // This could be the key we're looking for. + if m.keyEqual(key, hashmapSlotKeyData(m, slotKey), m.keySize) { + // Found the key, delete it. + bucket.tophash[i] = 0 + // Zero out the slot so the GC won't pin the allocations. + keySlotSize := hashmapKeySlotSize(m) + memzero(slotKey, keySlotSize) + slotValue := hashmapSlotValue(m, bucket, i) + valueSlotSize := hashmapValueSlotSize(m) + memzero(slotValue, valueSlotSize) + m.count-- + return + } + } + } + bucket = bucket.next + } +} + +// Iterate over a hashmap. +// +//go:nobounds +func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool { + if m == nil || m.count == 0 { + // From the spec: if the map is nil, the number of iterations is 0. + return false + } + if it.buckets == nil { + // initialize iterator + it.buckets = m.buckets + it.numBuckets = uintptr(1) << m.bucketBits + it.startBucket = uintptr(fastrand()) & (it.numBuckets - 1) + it.startIndex = uint8(fastrand() & 7) + + it.bucketNumber = it.startBucket + it.bucket = hashmapBucketAddr(m, it.buckets, it.bucketNumber) + it.bucketIndex = it.startIndex + } + + for { + // If we've wrapped and we're back at our starting location, terminate the iteration. + if it.wrapped && it.bucketNumber == it.startBucket && it.bucketIndex == it.startIndex { + return false + } + + if it.bucketIndex >= 8 { + // end of bucket, move to the next in the chain + it.bucketIndex = 0 + it.bucket = it.bucket.next + } + + if it.bucket == nil { + it.bucketNumber++ // next bucket + if it.bucketNumber >= it.numBuckets { + // went through all buckets -- wrap around + it.bucketNumber = 0 + it.wrapped = true + } + it.bucket = hashmapBucketAddr(m, it.buckets, it.bucketNumber) + continue + } + + if it.bucket.tophash[it.bucketIndex] == 0 { + // slot is empty - move on + it.bucketIndex++ + continue + } + + // Found a key. + slotKey := hashmapSlotKey(m, it.bucket, it.bucketIndex) + memcpy(key, hashmapSlotKeyData(m, slotKey), m.keySize) + + if it.buckets == m.buckets { + // Our view of the buckets is the same as the parent map. + // Just copy the value we have + slotValue := hashmapSlotValue(m, it.bucket, it.bucketIndex) + memcpy(value, hashmapSlotValueData(m, slotValue), m.valueSize) + it.bucketIndex++ + } else { + it.bucketIndex++ + + // Our view of the buckets doesn't match the parent map. + // Look up the key in the new buckets and return that value if it exists + hash := m.keyHash(key, m.keySize, m.seed) + ok := hashmapGet(m, key, value, m.valueSize, hash) + if !ok { + // doesn't exist in parent map; try next key + continue + } + + // All good. + } + + return true + } +} + +// Hashmap with plain binary data keys (not containing strings etc.). +func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { + if m == nil { + nilMapPanic() + } + hash := hash32(key, m.keySize, m.seed) + hashmapSet(m, key, value, hash) +} + +func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { + if m == nil || m.count == 0 { + memzero(value, uintptr(valueSize)) + return false + } + hash := hash32(key, m.keySize, m.seed) + return hashmapGet(m, key, value, valueSize, hash) +} + +func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { + if m == nil || m.count == 0 { + return + } + hash := hash32(key, m.keySize, m.seed) + hashmapDelete(m, key, hash) +} + +// Hashmap with compiler-generated key hash/equal functions. +// Unlike the binary path (which uses hash32/memequal), these use the +// type-specific keyHash and keyEqual function pointers stored in the hashmap +// struct. This is used for composite key types (e.g. structs containing +// strings) where the compiler generates specialized hash/equal functions. + +func hashmapGenericSet(m *hashmap, key, value unsafe.Pointer) { + if m == nil { + nilMapPanic() + } + hash := m.keyHash(key, m.keySize, m.seed) + hashmapSet(m, key, value, hash) +} + +func hashmapGenericGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { + if m == nil || m.count == 0 { + memzero(value, uintptr(valueSize)) + return false + } + hash := m.keyHash(key, m.keySize, m.seed) + return hashmapGet(m, key, value, valueSize, hash) +} + +func hashmapGenericDelete(m *hashmap, key unsafe.Pointer) { + if m == nil || m.count == 0 { + return + } + hash := m.keyHash(key, m.keySize, m.seed) + hashmapDelete(m, key, hash) +} + +// hashmapMakeGeneric creates a new hashmap with compiler-provided hash and +// equal functions. This avoids the interface/reflection path for composite +// key types like structs containing strings. +func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr, + typeInfo unsafe.Pointer, + keyHash func(key unsafe.Pointer, size, seed uintptr) uint32, + keyEqual func(x, y unsafe.Pointer, n uintptr) bool, +) *hashmap { + bucketBits := uint8(0) + if sizeHint > 8 { + for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) { + bucketBits++ + } + } + + var flags uint8 + keySlotSize := keySize + if keySize > hashmapMaxKeySize { + flags |= hashmapFlagIndirectKey + keySlotSize = unsafe.Sizeof(unsafe.Pointer(nil)) + } + valueSlotSize := valueSize + if valueSize > hashmapMaxValueSize { + flags |= hashmapFlagIndirectValue + valueSlotSize = unsafe.Sizeof(unsafe.Pointer(nil)) + } + + bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8 + buckets := alloc(bucketBufSize*(1< 0 { + // Allocate a new slice with capacity for elemsLen more elements, if necessary; + // otherwise, reuse the passed slice. + srcBuf, _, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize, layout) + + // Append the new elements in-place. + memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize) + } + + return srcBuf, newLen, srcCap +} + +// sliceGrow returns a new slice with space for at least newCap elements +func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) { + if oldCap >= newCap { + // No need to grow, return the input slice. + return oldBuf, oldLen, oldCap + } + if elemSize == 0 { + return oldBuf, oldLen, newCap + } + + // This can be made more memory-efficient by multiplying by some other constant, such as 1.5, + // which seems to be allowed by the Go language specification (but this can be observed by + // programs); however, due to memory fragmentation and the current state of the TinyGo + // memory allocators, this causes some difficult to debug issues. + if oldCap >= 256 && newCap <= oldCap+oldCap/2 { + newCap = oldCap + oldCap/2 + } else { + newCap = 1 << bits.Len(uint(newCap)) + } + + buf := alloc(newCap*elemSize, layout) + if oldLen > 0 { + // copy any data to new slice + memmove(buf, oldBuf, oldLen*elemSize) + } + + return buf, oldLen, newCap +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/sync/map.go b/tools/scripts/testdata/tinygo-overlay/src/sync/map.go new file mode 100644 index 0000000000000..99fa5c9ec9229 --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/sync/map.go @@ -0,0 +1,215 @@ +package sync + +import ( + "internal/task" +) + +// This file implements just enough of sync.Map to get packages to compile. It +// is no more efficient than a map with a lock. + +type Map struct { + lock task.PMutex + m map[interface{}]interface{} + postMark map[interface{}]interface{} + epoch uint32 +} + +func (m *Map) activeMap() map[interface{}]interface{} { + ep := poolEpoch() + if ep <= 1 { + if m.m == nil { + m.m = make(map[interface{}]interface{}) + } + return m.m + } + if m.epoch != ep || m.postMark == nil { + m.postMark = make(map[interface{}]interface{}) + m.epoch = ep + } + return m.postMark +} + +func (m *Map) Delete(key interface{}) { + m.lock.Lock() + defer m.lock.Unlock() + ep := poolEpoch() + if ep <= 1 { + delete(m.m, key) + } else if m.epoch == ep && m.postMark != nil { + delete(m.postMark, key) + } +} + +func (m *Map) Load(key interface{}) (value interface{}, ok bool) { + m.lock.Lock() + defer m.lock.Unlock() + if m.m != nil { + if value, ok = m.m[key]; ok { + return value, true + } + } + ep := poolEpoch() + if ep > 1 && m.epoch == ep && m.postMark != nil { + value, ok = m.postMark[key] + } + return +} + +func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { + m.lock.Lock() + defer m.lock.Unlock() + if m.m != nil { + if existing, ok := m.m[key]; ok { + return existing, true + } + } + target := m.activeMap() + if existing, ok := target[key]; ok { + return existing, true + } + target[key] = value + return value, false +} + +func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { + m.lock.Lock() + defer m.lock.Unlock() + ep := poolEpoch() + if ep <= 1 { + value, ok := m.m[key] + if !ok { + return nil, false + } + delete(m.m, key) + return value, true + } + if m.epoch == ep && m.postMark != nil { + if value, ok := m.postMark[key]; ok { + delete(m.postMark, key) + return value, true + } + } + return nil, false +} + +func (m *Map) Store(key, value interface{}) { + m.lock.Lock() + defer m.lock.Unlock() + target := m.activeMap() + target[key] = value +} + +// Range calls f for each key and value in the map. If f returns false, the iteration stops. +func (m *Map) Range(f func(key, value interface{}) bool) { + m.lock.Lock() + keys := make([]interface{}, 0, len(m.m)) + for k := range m.m { + keys = append(keys, k) + } + ep := poolEpoch() + if ep > 1 && m.epoch == ep { + for k := range m.postMark { + if _, ok := m.m[k]; !ok { + keys = append(keys, k) + } + } + } + m.lock.Unlock() + + for _, k := range keys { + if v, ok := m.Load(k); ok { + if !f(k, v) { + break + } + } + } +} + +func (m *Map) Clear() { + m.lock.Lock() + defer m.lock.Unlock() + ep := poolEpoch() + if ep <= 1 { + clear(m.m) + } else { + m.postMark = nil + m.epoch = ep + } +} + +// Swap replaces the value for the given key, and returns the old value if any. +func (m *Map) Swap(key, value any) (previous any, loaded bool) { + m.lock.Lock() + defer m.lock.Unlock() + if m.m != nil { + if prev, ok := m.m[key]; ok { + previous, loaded = prev, true + } + } + target := m.activeMap() + if prev, ok := target[key]; ok { + previous, loaded = prev, true + } + target[key] = value + return +} + +// CompareAndSwap swaps the old and new values for an existing key if the value +// stored in the map is equal to old. +func (m *Map) CompareAndSwap(key, old, new any) (swapped bool) { + m.lock.Lock() + defer m.lock.Unlock() + ep := poolEpoch() + if ep <= 1 { + if m.m == nil { + return false + } + value, ok := m.m[key] + if !ok || value != old { + return false + } + m.m[key] = new + return true + } + var value any + var ok bool + if m.epoch == ep && m.postMark != nil { + value, ok = m.postMark[key] + } + if !ok && m.m != nil { + value, ok = m.m[key] + } + if !ok || value != old { + return false + } + target := m.activeMap() + target[key] = new + return true +} + +// CompareAndDelete deletes the entry for key if its value is equal to old. +func (m *Map) CompareAndDelete(key, old any) (deleted bool) { + m.lock.Lock() + defer m.lock.Unlock() + ep := poolEpoch() + if ep <= 1 { + if m.m == nil { + return false + } + value, ok := m.m[key] + if !ok || value != old { + return false + } + delete(m.m, key) + return true + } + if m.epoch != ep || m.postMark == nil { + return false + } + value, ok := m.postMark[key] + if !ok || value != old { + return false + } + delete(m.postMark, key) + return true +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/sync/once.go b/tools/scripts/testdata/tinygo-overlay/src/sync/once.go new file mode 100644 index 0000000000000..598e9325c09c4 --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/sync/once.go @@ -0,0 +1,17 @@ +package sync + +type Once struct { + epoch uint32 + m Mutex +} + +func (o *Once) Do(f func()) { + o.m.Lock() + defer o.m.Unlock() + ep := poolEpoch() + if o.epoch == 1 || o.epoch == ep { + return + } + o.epoch = ep + f() +} diff --git a/tools/scripts/testdata/tinygo-overlay/src/sync/pool.go b/tools/scripts/testdata/tinygo-overlay/src/sync/pool.go new file mode 100644 index 0000000000000..539b8d0d3e836 --- /dev/null +++ b/tools/scripts/testdata/tinygo-overlay/src/sync/pool.go @@ -0,0 +1,54 @@ +package sync + +import ( + "internal/task" + _ "unsafe" +) + +//go:linkname poolEpoch runtime.PoolEpoch +func poolEpoch() uint32 + +// Pool is a very simple implementation of sync.Pool. +type Pool struct { + lock task.PMutex + New func() interface{} + items []interface{} + epoch uint32 +} + +// Get returns an item in the pool, or the value of calling Pool.New() if there +// are no items. +func (p *Pool) Get() interface{} { + p.lock.Lock() + ep := poolEpoch() + if p.epoch != ep { + p.items = nil + p.epoch = ep + } + if len(p.items) > 0 { + x := p.items[len(p.items)-1] + p.items = p.items[:len(p.items)-1] + p.lock.Unlock() + return x + } + p.lock.Unlock() + if p.New != nil { + return p.New() + } + return nil +} + +// Put adds a value back into the pool. +func (p *Pool) Put(x interface{}) { + if x == nil { + return + } + p.lock.Lock() + ep := poolEpoch() + if p.epoch != ep { + p.items = nil + p.epoch = ep + } + p.items = append(p.items, x) + p.lock.Unlock() +} diff --git a/tools/scripts/tsc/ast.json b/tools/scripts/tsc/ast.json index a133915a10665..ba328f9bff3c3 100644 --- a/tools/scripts/tsc/ast.json +++ b/tools/scripts/tsc/ast.json @@ -945,12 +945,6 @@ "extends": [] }, "CompositeBase": { - "fields": { - "facts": { - "type": "atomic.Uint32", - "goOnly": true - } - }, "extends": [] }, "TypeSyntaxBase": { @@ -1743,7 +1737,8 @@ "type": "BindingElement", "list": "NodeList" } - ] + ], + "arena": true }, "ParameterDeclaration": { "kind": "Parameter", @@ -1816,7 +1811,8 @@ "type": "Expression", "optional": true } - ] + ], + "arena": true }, "MissingDeclaration": { "extends": [ @@ -2141,7 +2137,8 @@ "type": "ImportAttributes", "optional": true } - ] + ], + "arena": true }, "ExternalModuleReference": { "generateSubtreeFacts": true, @@ -2182,7 +2179,8 @@ "type": "ImportSpecifier", "list": "NodeList" } - ] + ], + "arena": true }, "ExportAssignment": { "extends": [ @@ -2577,7 +2575,8 @@ "optional": true, "visit": "functionBody" } - ] + ], + "arena": true }, "PropertySignatureDeclaration": { "kind": "PropertySignature", @@ -2643,7 +2642,8 @@ "type": "Expression", "optional": true } - ] + ], + "arena": true }, "SemicolonClassElement": { "extends": [ @@ -2930,7 +2930,8 @@ "type": "ConciseBody", "visit": "functionBody" } - ] + ], + "arena": true }, "FunctionExpression": { "extends": [ @@ -3001,7 +3002,8 @@ "name": "Type", "type": "TypeNode" } - ] + ], + "arena": true }, "SatisfiesExpression": { "extends": [ @@ -3229,7 +3231,8 @@ "type": "TemplateSpan", "list": "NodeList" } - ] + ], + "arena": true }, "TemplateSpan": { "generateSubtreeFacts": true, @@ -3245,7 +3248,8 @@ "name": "Literal", "type": "TemplateMiddleOrTail" } - ] + ], + "arena": true }, "TaggedTemplateExpression": { "extends": [ @@ -3307,7 +3311,8 @@ "name": "MultiLine", "type": "bool" } - ] + ], + "arena": true }, "ObjectLiteralExpression": { "generateSubtreeFacts": true, @@ -3326,7 +3331,8 @@ "name": "MultiLine", "type": "bool" } - ] + ], + "arena": true }, "SpreadAssignment": { "extends": [ @@ -3408,7 +3414,8 @@ "type": "Expression", "optional": true } - ] + ], + "arena": true }, "DeleteExpression": { "generateSubtreeFacts": true, @@ -3432,7 +3439,8 @@ "name": "Expression", "type": "Expression" } - ] + ], + "arena": true }, "VoidExpression": { "generateSubtreeFacts": true, @@ -3455,7 +3463,8 @@ "name": "Expression", "type": "Expression" } - ] + ], + "arena": true }, "TypeAssertion": { "kind": "TypeAssertionExpression", @@ -3542,7 +3551,8 @@ "name": "FalseType", "type": "TypeNode" } - ] + ], + "arena": true }, "TypeOperatorNode": { "kind": "TypeOperator", @@ -3575,7 +3585,8 @@ "name": "TypeParameter", "type": "TypeParameterDeclaration" } - ] + ], + "arena": true }, "ArrayTypeNode": { "kind": "ArrayType", @@ -3660,7 +3671,8 @@ "kind": "ThisType", "extends": [ "TypeNodeBase" - ] + ], + "arena": true }, "TypePredicateNode": { "kind": "TypePredicate", @@ -3741,7 +3753,8 @@ "name": "TypeArguments", "inherited": true } - ] + ], + "arena": true }, "MappedTypeNode": { "kind": "MappedType", @@ -3819,7 +3832,8 @@ "type": "TypeNode", "list": "NodeList" } - ] + ], + "arena": true }, "NamedTupleMember": { "extends": [ @@ -3870,7 +3884,8 @@ "name": "Type", "type": "TypeNode" } - ] + ], + "arena": true }, "ParenthesizedTypeNode": { "kind": "ParenthesizedType", @@ -3949,7 +3964,8 @@ "inherited": true, "bitmask": "TokenFlagsTemplateLiteralLikeFlags" } - ] + ], + "arena": true }, "TemplateMiddle": { "extends": [ @@ -3970,7 +3986,8 @@ "inherited": true, "bitmask": "TokenFlagsTemplateLiteralLikeFlags" } - ] + ], + "arena": true }, "TemplateTail": { "extends": [ @@ -3991,7 +4008,8 @@ "inherited": true, "bitmask": "TokenFlagsTemplateLiteralLikeFlags" } - ] + ], + "arena": true }, "TemplateLiteralTypeNode": { "kind": "TemplateLiteralType", @@ -4008,7 +4026,8 @@ "type": "TemplateLiteralTypeSpan", "list": "NodeList" } - ] + ], + "arena": true }, "TemplateLiteralTypeSpan": { "extends": [ @@ -4023,7 +4042,8 @@ "name": "Literal", "type": "TemplateMiddleOrTail" } - ] + ], + "arena": true }, "SyntheticExpression": { "extends": [ @@ -4916,7 +4936,8 @@ "name": "TypeArguments", "inherited": true } - ] + ], + "arena": true }, "ImportClause": { "extends": [ @@ -4942,7 +4963,8 @@ "type": "NamedImportBindings", "optional": true } - ] + ], + "arena": true }, "ImportSpecifier": { "extends": [ diff --git a/tools/scripts/tsc/generate-encoder.ts b/tools/scripts/tsc/generate-encoder.ts index 1b106bb8ff555..909ab13103e6d 100644 --- a/tools/scripts/tsc/generate-encoder.ts +++ b/tools/scripts/tsc/generate-encoder.ts @@ -2059,40 +2059,44 @@ function writeAndFormat(filePath: string, generate: () => string, force: boolean console.log(`Wrote ${filePath}`); } -export default function main(force = false) { - console.log("Generating encoder/decoder code..."); +export const outputs = [ + "tsc/internal/api/encoder/encoder_generated.go", + "tsc/internal/api/encoder/decoder_generated.go", + "packages/typescript/src/api/node/protocol.generated.ts", + "packages/typescript/src/api/node/encoder.generated.ts", + "packages/typescript/src/api/node/node.generated.ts", +] as const; +export default function main(force = false) { writeAndFormat( - path.join(ROOT, "tsc/internal/api/encoder/encoder_generated.go"), + path.join(ROOT, outputs[0]), generateGoEncoder, force, ); writeAndFormat( - path.join(ROOT, "tsc/internal/api/encoder/decoder_generated.go"), + path.join(ROOT, outputs[1]), generateGoDecoder, force, ); writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/protocol.generated.ts"), + path.join(ROOT, outputs[2]), generateTSProtocol, force, ); writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/encoder.generated.ts"), + path.join(ROOT, outputs[3]), generateTSEncoder, force, ); writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/node.generated.ts"), + path.join(ROOT, outputs[4]), generateTSNodeGenerated, force, ); - - console.log("Done!"); } if (process.argv[1] === fileURLToPath(import.meta.url)) { diff --git a/tools/scripts/tsc/generate-enums.ts b/tools/scripts/tsc/generate-enums.ts index 2f2a315e4b49f..3a5c04d7c8cbf 100644 --- a/tools/scripts/tsc/generate-enums.ts +++ b/tools/scripts/tsc/generate-enums.ts @@ -373,6 +373,14 @@ export function generateEnum(def: EnumDef): { members: EnumMember[]; code: strin const enumValuesGeneratedGoPath = path.join(ROOT, "tsc/internal/api/enum_values_generated.go"); +export const outputs = [ + enumValuesGeneratedGoPath, + ...enumDefs.flatMap(def => { + const camelName = def.name.charAt(0).toLowerCase() + def.name.slice(1); + return [path.join(ROOT, def.outDir, `${camelName}.enum.ts`), path.join(ROOT, def.outDir, `${camelName}.ts`)]; + }), +]; + interface GeneratedEnum { def: EnumDef; code: string; @@ -500,7 +508,6 @@ export default async function generateEnums(force = false) { const generatedGoFile = new GeneratedFile(enumValuesGeneratedGoPath, [...inputs, ...enumDefs.map(def => path.join(ROOT, def.goFile))]); const generatedFiles = [generatedGoFile, ...enumFiles.flatMap(({ typeFile, runtimeFile }) => [typeFile, runtimeFile])]; if (generatedFiles.every(file => file.isCurrent(force))) { - console.log("Enums are up to date."); return; } const ts = await import("typescript"); diff --git a/tools/scripts/tsc/generate-go-ast.ts b/tools/scripts/tsc/generate-go-ast.ts index db4d110de8f8a..938c0457884d8 100644 --- a/tools/scripts/tsc/generate-go-ast.ts +++ b/tools/scripts/tsc/generate-go-ast.ts @@ -198,9 +198,6 @@ function verifyNodeBaseAtOffsetZero(): void { } } -verifyNoDuplicateBases(); -verifyNodeBaseAtOffsetZero(); - // A base generated without fields or embeddings is a struct{} marker. Go pads a struct whose last field is // zero-size (so the field's address stays in bounds), so markers are embedded first, never last; being // zero-size, they don't move NodeBase off offset zero. @@ -859,7 +856,7 @@ function transitiveBaseKeys(node: NodeType): Set { return seen; } -function generateNodeAccessorDispatch(w: CodeWriter, method: string, ret: string, hasAccessor: (node: NodeType) => boolean) { +function generateNodeAccessorDispatch(w: CodeWriter, method: string, ret: string, field: string, deref: boolean, hasAccessor: (node: NodeType) => boolean) { const cases: { node: NodeType; kinds: string[]; }[] = []; for (const node of api.nodes()) { if (!hasAccessor(node)) continue; @@ -867,45 +864,31 @@ function generateNodeAccessorDispatch(w: CodeWriter, method: string, ret: string if (kinds.length !== 0) cases.push({ node, kinds }); } - // Large sparse Kind switches become comparison trees. Dense indices let Go - // emit jump tables; zero is reserved for kinds without the accessor. - const useDispatchTable = cases.length >= 8; const table = `node${method}Dispatch`; - if (useDispatchTable) { - if (cases.length > 255) { - throw new Error(`${method} dispatch exceeds the uint8 index range`); - } - w.write(`var ${table} = [kindFlowReduceLabelData + 1]uint8{`); - w.push(); - for (const [index, { kinds }] of cases.entries()) { - for (const kind of kinds) { - w.write(`${kind}: ${index + 1},`); - } + w.write(`var ${table} = [kindFlowReduceLabelData + 1]uint8{`); + w.push(); + for (const { node, kinds } of cases) { + for (const kind of kinds) { + w.write(`${kind}: uint8(unsafe.Offsetof(${node.name}{}.${field})),`); } - w.pop(); - w.write("}"); - w.write(""); } + w.pop(); + w.write("}"); + w.write(""); w.write(`func (n *Node) ${method}() ${ret} {`); w.push(); - if (useDispatchTable) { - w.write(`switch ${table}[n.Kind] {`); + w.write(`if offset := ${table}[n.Kind]; offset != 0 {`); + w.push(); + if (deref) { + w.write(`return *(*${ret})(unsafe.Add(unsafe.Pointer(n), offset))`); } else { - w.write("switch n.Kind {"); - } - for (const [index, { node, kinds }] of cases.entries()) { - w.write(`case ${useDispatchTable ? index + 1 : kinds.join(", ")}:`); - w.push(); - w.write(`return (*${node.name})(unsafe.Pointer(n)).${method}()`); - w.pop(); + w.write(`return (${ret})(unsafe.Add(unsafe.Pointer(n), offset))`); } - w.write("default:"); - w.push(); - w.write("return nil"); w.pop(); w.write("}"); + w.write("return nil"); w.pop(); w.write("}"); w.write(""); @@ -913,7 +896,7 @@ function generateNodeAccessorDispatch(w: CodeWriter, method: string, ret: string function generateNodeAccessors(w: CodeWriter) { for (const { method, ret, base } of NODE_ACCESSORS) { - generateNodeAccessorDispatch(w, method, ret, node => transitiveBaseKeys(node).has(base)); + generateNodeAccessorDispatch(w, method, ret, base, false, node => transitiveBaseKeys(node).has(base)); } } @@ -921,27 +904,13 @@ function hasMember(node: NodeType, name: string): boolean { return schemaMembers(node).some(member => member.name === name); } -function generateNameDispatch(w: CodeWriter) { - generateNodeAccessorDispatch(w, "Name", "*DeclarationName", node => hasMember(node, "name")); -} - -function generateModifiersDispatch(w: CodeWriter) { - generateNodeAccessorDispatch(w, "Modifiers", "*ModifierList", node => hasMember(node, "modifiers")); -} - function generateSetModifiersDispatch(w: CodeWriter) { w.write("func (n *MutableNode) SetModifiers(modifiers *ModifierList) {"); w.push(); - w.write("switch n.Kind {"); - for (const node of api.nodes()) { - if (!hasMember(node, "modifiers")) continue; - const kinds = node.allKinds().map(kind => kind.formatGoConstant()); - if (kinds.length === 0) continue; - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`(*${node.name})(unsafe.Pointer(n)).setModifiers(modifiers)`); - w.pop(); - } + w.write("if offset := nodeModifiersDispatch[n.Kind]; offset != 0 {"); + w.push(); + w.write("*(**ModifierList)(unsafe.Add(unsafe.Pointer(n), offset)) = modifiers"); + w.pop(); w.write("}"); w.pop(); w.write("}"); @@ -1331,8 +1300,8 @@ function generate(): string { w.write("// Common node accessor dispatch"); w.write("// ──────────────────────────────────────────────────────────────────────"); w.write(""); - generateNameDispatch(w); - generateModifiersDispatch(w); + generateNodeAccessorDispatch(w, "Name", "*DeclarationName", "name", true, node => hasMember(node, "name")); + generateNodeAccessorDispatch(w, "Modifiers", "*ModifierList", "modifiers", true, node => hasMember(node, "modifiers")); generateSetModifiersDispatch(w); // As*() casts @@ -1483,13 +1452,16 @@ function writeAndFormat(filePath: string, generateContent: () => string, force: console.log(`Wrote ${filePath}`); } +export const outputs = ["tsc/internal/ast/ast_generated.go", "tsc/internal/ast/kind_generated.go"] as const; + export default function main(force = false) { - console.log("Generating Go AST code..."); + verifyNoDuplicateBases(); + verifyNodeBaseAtOffsetZero(); - const outPath = path.join(ROOT, "tsc/internal/ast/ast_generated.go"); + const outPath = path.join(ROOT, outputs[0]); writeAndFormat(outPath, () => generate() + "\n", force); - const kindOutPath = path.join(ROOT, "tsc/internal/ast/kind_generated.go"); + const kindOutPath = path.join(ROOT, outputs[1]); writeAndFormat(kindOutPath, () => generateKind() + "\n", force); } diff --git a/tools/scripts/tsc/generate-ts-ast.ts b/tools/scripts/tsc/generate-ts-ast.ts index 434d6e81ed03c..2a738ea86ac4d 100644 --- a/tools/scripts/tsc/generate-ts-ast.ts +++ b/tools/scripts/tsc/generate-ts-ast.ts @@ -42,7 +42,7 @@ function isInheritedFromTsBase(member: MemberInfo): boolean { // NodeBase fields (e.g. Flags) are class-level properties on NodeObject, // not data members stored in _data. -const nodeBaseFieldNames = new Set( +let nodeBaseFieldNames = new Set( api.bases().find(b => b.name === "NodeBase")?.fields.filter(f => !f.noTS).map(f => f.name) ?? [], ); @@ -108,7 +108,7 @@ function syntaxKindChecksForNode(node: NodeType): string[] { // Bases for ast.generated.ts // ──────────────────────────────────────────────────────────────────────────── -const goOnlyBases = new Set( +let goOnlyBases = new Set( api.bases().filter(base => base.isGoOnly()).map(base => base.key), ); @@ -217,7 +217,7 @@ function computeTsVariants(): TsVariant[] { return result; } -const tsVariants = computeTsVariants(); +let tsVariants = computeTsVariants(); function visitorNodes(): NodeType[] { return api.nodes().filter(node => !isVariantNode(node)); @@ -1823,13 +1823,22 @@ function writeAndFormat(filePath: string, generate: () => string, force: boolean console.log(`Generated ${filePath}`); } -export default function main(force = false) { - console.log("Generating TS AST code..."); +export const outputs = [ + "packages/typescript/src/ast/factory.generated.ts", + "packages/typescript/src/ast/is.generated.ts", + "packages/typescript/src/ast/ast.generated.ts", + "packages/typescript/src/ast/visitor.generated.ts", +] as const; - const factoryPath = path.join(ROOT, "packages/typescript/src/ast/factory.generated.ts"); - const isGenPath = path.join(ROOT, "packages/typescript/src/ast/is.generated.ts"); - const astGenPath = path.join(ROOT, "packages/typescript/src/ast/ast.generated.ts"); - const visitorPath = path.join(ROOT, "packages/typescript/src/ast/visitor.generated.ts"); +export default function main(force = false) { + nodeBaseFieldNames = new Set(api.bases().find(base => base.name === "NodeBase")?.fields.filter(field => !field.noTS).map(field => field.name) ?? []); + goOnlyBases = new Set(api.bases().filter(base => base.isGoOnly()).map(base => base.key)); + tsVariants = computeTsVariants(); + + const factoryPath = path.join(ROOT, outputs[0]); + const isGenPath = path.join(ROOT, outputs[1]); + const astGenPath = path.join(ROOT, outputs[2]); + const visitorPath = path.join(ROOT, outputs[3]); writeAndFormat(astGenPath, generateAstGenerated, force); writeAndFormat(factoryPath, generateFactory, force); diff --git a/tools/scripts/tsc/generate.ts b/tools/scripts/tsc/generate.ts index 65f2a3997980a..434db91c38ca1 100644 --- a/tools/scripts/tsc/generate.ts +++ b/tools/scripts/tsc/generate.ts @@ -3,8 +3,10 @@ import { parseGeneratorArgs } from "../gen/utils.mts"; import generateEncoder from "./generate-encoder.ts"; import generateGoAST from "./generate-go-ast.ts"; import generateTSAST from "./generate-ts-ast.ts"; +import { reloadSchema } from "./schema.ts"; export default function generate(force = false) { + reloadSchema(); generateEncoder(force); generateGoAST(force); generateTSAST(force); diff --git a/tools/scripts/tsc/options.ts b/tools/scripts/tsc/options.ts index 6ebec633e1931..3f27b59602fbe 100644 --- a/tools/scripts/tsc/options.ts +++ b/tools/scripts/tsc/options.ts @@ -1626,6 +1626,17 @@ export const options: OptionsModel = { minValue: 1, }, }, + { + name: "maxMemory", + type: "*int", + internal: true, + declaration: { + group: "commonOptionsWithBuild", + category: diagnostic("Command-line Options"), + description: diagnostic("Set the memory limit in MiB for recycling checkers during no-emit type checking."), + minValue: 1, + }, + }, ], // Preserve help and config output order independently of compiler fields. declarationOrder: { @@ -1657,6 +1668,7 @@ export const options: OptionsModel = { "singleThreaded", "pprofDir", "checkers", + "maxMemory", "runExternalCode", ], optionsForCompiler: [ diff --git a/tools/scripts/tsc/schema.ts b/tools/scripts/tsc/schema.ts index 6d2985d433435..1dcaf10796f02 100644 --- a/tools/scripts/tsc/schema.ts +++ b/tools/scripts/tsc/schema.ts @@ -1261,7 +1261,16 @@ export class SchemaAPI { } } -export const api = new SchemaAPI(JSON.parse( - fs.readFileSync(path.join(ROOT, "tools/scripts/tsc/ast.json"), "utf-8"), -) as Schema); -api.validate(); +function readSchema(): SchemaAPI { + const schema = new SchemaAPI(JSON.parse( + fs.readFileSync(path.join(ROOT, "tools/scripts/tsc/ast.json"), "utf-8"), + ) as Schema); + schema.validate(); + return schema; +} + +export let api = readSchema(); + +export function reloadSchema(): void { + api = readSchema(); +} diff --git a/tsc/cmd/tsc/api.go b/tsc/cmd/tsc/api.go index fc5934e0eefcb..43ea87764adb9 100644 --- a/tsc/cmd/tsc/api.go +++ b/tsc/cmd/tsc/api.go @@ -1,3 +1,5 @@ +//go:build !wasip1 + package main import ( diff --git a/tsc/cmd/tsc/default.pgo b/tsc/cmd/tsc/default.pgo new file mode 100644 index 0000000000000..5a8a51c70ac72 Binary files /dev/null and b/tsc/cmd/tsc/default.pgo differ diff --git a/tsc/cmd/tsc/lsp.go b/tsc/cmd/tsc/lsp.go index 3bb3affd2b160..3afd11dcbdd61 100644 --- a/tsc/cmd/tsc/lsp.go +++ b/tsc/cmd/tsc/lsp.go @@ -1,3 +1,5 @@ +//go:build !wasip1 + package main import ( diff --git a/tsc/cmd/tsc/main.go b/tsc/cmd/tsc/main.go index cbe3f0f4e567b..831d3325b0c67 100644 --- a/tsc/cmd/tsc/main.go +++ b/tsc/cmd/tsc/main.go @@ -1,9 +1,12 @@ +//go:build !wasip1 + package main import ( "context" "os" "os/signal" + "runtime/debug" "syscall" "github.com/microsoft/TypeScript/tsc/internal/core" @@ -17,6 +20,12 @@ func main() { func runMain() int { core.ApplyDebugStackLimit() + if os.Getenv("GOGC") == "" { + debug.SetGCPercent(300) + } + if os.Getenv("GOMEMLIMIT") == "" { + debug.SetMemoryLimit(8 << 30) + } args := osutil.Args()[1:] if len(args) > 0 { switch args[0] { diff --git a/tsc/cmd/tsc/sys.go b/tsc/cmd/tsc/sys.go index dc8198a564c9a..325592613ff6b 100644 --- a/tsc/cmd/tsc/sys.go +++ b/tsc/cmd/tsc/sys.go @@ -1,3 +1,5 @@ +//go:build !wasip1 + package main import ( diff --git a/tsc/cmd/tsc/wasm_wasip1.go b/tsc/cmd/tsc/wasm_wasip1.go new file mode 100644 index 0000000000000..8ab0ce00a371c --- /dev/null +++ b/tsc/cmd/tsc/wasm_wasip1.go @@ -0,0 +1,481 @@ +//go:build wasip1 + +package main + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "io/fs" + "os" + "slices" + "strconv" + "strings" + "time" + "unicode/utf16" + "unsafe" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/bundled" + "github.com/microsoft/TypeScript/tsc/internal/compiler" + "github.com/microsoft/TypeScript/tsc/internal/core" + "github.com/microsoft/TypeScript/tsc/internal/diagnostics" + "github.com/microsoft/TypeScript/tsc/internal/diagnosticwriter" + "github.com/microsoft/TypeScript/tsc/internal/execute" + "github.com/microsoft/TypeScript/tsc/internal/json" + "github.com/microsoft/TypeScript/tsc/internal/locale" + "github.com/microsoft/TypeScript/tsc/internal/scanner" + "github.com/microsoft/TypeScript/tsc/internal/tspath" + "github.com/microsoft/TypeScript/tsc/internal/vfs" +) + +const ( + opRead uint32 = 0 + opStat uint32 = 1 + opReadDir uint32 = 2 + opRealpath uint32 = 3 + opWrite uint32 = 4 + opAppend uint32 = 5 + opRemove uint32 = 6 + opChtimes uint32 = 7 +) + +//go:wasmimport tsrs_host fs +func hostFS(op uint32, ptr unsafe.Pointer, length uint32) int32 + +//go:wasmimport tsrs_host fs_take +func hostFSTake(ptr unsafe.Pointer) + +//go:linkname runtimeMarkHeap runtime.MarkHeap +func runtimeMarkHeap() + +//go:linkname runtimeResetHeap runtime.ResetHeap +func runtimeResetHeap() + +const ( + wasmInitialInputCap = 4 << 20 + wasmInitialOutputCap = 8 << 20 +) + +var ( + wasmInputBuf []byte + wasmOutputBuf []byte + wasmDummyByte byte + wasmBuffersInitialized bool +) + +func init() { + compiler.SetWasmHeapHooks(func() { + _, _ = json.Marshal([]wasmDiagnosticResponse{{ + StartPosition: &wasmDiagnosticPosition{}, + EndPosition: &wasmDiagnosticPosition{}, + MessageChain: []wasmDiagnosticResponse{{}}, + RelatedInformation: []wasmDiagnosticResponse{{}}, + }}) + runtimeMarkHeap() + }, runtimeResetHeap) +} + +func ensureWasmBuffers() { + if !wasmBuffersInitialized { + wasmInputBuf = make([]byte, 0, wasmInitialInputCap) + wasmOutputBuf = make([]byte, 0, wasmInitialOutputCap) + wasmBuffersInitialized = true + } +} + +//go:wasmexport tsrs_input +func tsrsInput(length uint32) unsafe.Pointer { + ensureWasmBuffers() + if int(length) <= cap(wasmInputBuf) { + wasmInputBuf = wasmInputBuf[:length] + } else { + wasmInputBuf = make([]byte, max(int(length), 1))[:length] + } + if length == 0 { + return unsafe.Pointer(&wasmDummyByte) + } + return unsafe.Pointer(unsafe.SliceData(wasmInputBuf)) +} + +//go:wasmexport tsrs_run +func tsrsRun() int32 { + ensureWasmBuffers() + req := string(wasmInputBuf) + wasmInputBuf = wasmInputBuf[:0] + wasmOutputBuf = wasmOutputBuf[:0] + + parts := strings.Split(req, "\x00") + cwd := "/" + if len(parts) > 0 && parts[0] != "" { + cwd = parts[0] + } + var flags uint32 + if len(parts) > 1 { + if v, err := strconv.ParseUint(parts[1], 10, 32); err == nil { + flags = uint32(v) + } + } + var args []string + if len(parts) > 2 { + args = parts[2:] + } + + jsonDiagnostics := (flags & 1) != 0 + caseSensitive := (flags & 2) == 0 + tty := (flags & 4) != 0 + var writer io.Writer = os.Stdout + if jsonDiagnostics { + writer = io.Discard + } + sys := &wasmSys{ + cwd: tspath.RootedDirectoryPathFromAbsolute(tspath.NormalizePath(cwd)), + fs: bundled.WrapFS(&wasmHostFS{caseSensitive: caseSensitive}), + defaultLibraryPath: bundled.LibPath(), + writer: writer, + tty: tty, + start: time.Now(), + } + result := execute.CommandLine(context.Background(), sys, args, nil) + if jsonDiagnostics { + responses := make([]wasmDiagnosticResponse, len(result.Diagnostics)) + for i, d := range result.Diagnostics { + responses[i] = newWasmDiagnosticResponse(diagnosticwriter.WrapASTDiagnostic(d)) + } + if encoded, err := json.Marshal(responses); err == nil { + if len(encoded) <= cap(wasmOutputBuf) { + wasmOutputBuf = wasmOutputBuf[:len(encoded)] + copy(wasmOutputBuf, encoded) + } else { + wasmOutputBuf = encoded + } + } + } + compiler.ResetWasmHeapToWatermark() + return int32(result.Status) +} + +type wasmDiagnosticPosition struct { + Line int `json:"line"` + Character int `json:"character"` +} + +type wasmDiagnosticResponse struct { + FileName string `json:"fileName,omitempty"` + Pos int `json:"pos"` + End int `json:"end"` + StartPosition *wasmDiagnosticPosition `json:"startPosition,omitempty"` + EndPosition *wasmDiagnosticPosition `json:"endPosition,omitempty"` + Code int32 `json:"code"` + Category diagnostics.Category `json:"category"` + Source string `json:"source,omitempty"` + Text string `json:"text"` + ReportsUnnecessary bool `json:"reportsUnnecessary,omitempty"` + ReportsDeprecated bool `json:"reportsDeprecated,omitempty"` + MessageChain []wasmDiagnosticResponse `json:"messageChain,omitempty"` + RelatedInformation []wasmDiagnosticResponse `json:"relatedInformation,omitempty"` +} + +func newWasmDiagnosticResponse(d *diagnosticwriter.ASTDiagnostic) wasmDiagnosticResponse { + file := d.File() + pos, end := d.Pos(), d.End() + if file != nil { + pos = max(0, min(pos, len(file.Text()))) + end = max(pos, min(end, len(file.Text()))) + } + resp := wasmDiagnosticResponse{ + Pos: pos, + End: end, + Code: d.Code(), + Category: d.Category(), + Source: d.Source(), + Text: d.Localize(locale.Default), + ReportsUnnecessary: d.Diagnostic.ReportsUnnecessary(), + ReportsDeprecated: d.Diagnostic.ReportsDeprecated(), + } + if file != nil { + resp.FileName = file.FileName().AsString() + if sourceFile, ok := file.(*ast.SourceFile); ok { + positionMap := sourceFile.GetPositionMap() + resp.Pos = positionMap.UTF8ToUTF16(pos) + resp.End = positionMap.UTF8ToUTF16(end) + } else { + resp.Pos = int(core.UTF16Len(file.Text()[:pos])) + resp.End = int(core.UTF16Len(file.Text()[:end])) + } + startLine, startChar := scanner.GetECMALineAndUTF16CharacterOfPosition(file, pos) + endLine, endChar := scanner.GetECMALineAndUTF16CharacterOfPosition(file, end) + resp.StartPosition = &wasmDiagnosticPosition{Line: startLine, Character: int(startChar)} + resp.EndPosition = &wasmDiagnosticPosition{Line: endLine, Character: int(endChar)} + } + if chain := d.MessageChain(); len(chain) > 0 { + resp.MessageChain = make([]wasmDiagnosticResponse, len(chain)) + for i, c := range chain { + resp.MessageChain[i] = newWasmDiagnosticResponse(c.(*diagnosticwriter.ASTDiagnostic)) + } + } + if related := d.RelatedInformation(); len(related) > 0 { + resp.RelatedInformation = make([]wasmDiagnosticResponse, len(related)) + for i, r := range related { + resp.RelatedInformation[i] = newWasmDiagnosticResponse(r.(*diagnosticwriter.ASTDiagnostic)) + } + } + return resp +} + +//go:wasmexport tsrs_output +func tsrsOutput() unsafe.Pointer { + if len(wasmOutputBuf) == 0 { + return unsafe.Pointer(&wasmDummyByte) + } + return unsafe.Pointer(unsafe.SliceData(wasmOutputBuf)) +} + +//go:wasmexport tsrs_output_len +func tsrsOutputLen() uint32 { + return uint32(len(wasmOutputBuf)) +} + +func hostCall(op uint32, arg string) ([]byte, error) { + var ptr unsafe.Pointer + if len(arg) > 0 { + ptr = unsafe.Pointer(unsafe.StringData(arg)) + } else { + ptr = unsafe.Pointer(&wasmDummyByte) + } + n := hostFS(op, ptr, uint32(len(arg))) + if n == 0 { + return nil, nil + } + if n > 0 { + buf := make([]byte, n) + hostFSTake(unsafe.Pointer(unsafe.SliceData(buf))) + return buf, nil + } + if n == -1 { + return nil, vfs.ErrNotExist + } + errLen := -2 - n + if errLen <= 0 { + return nil, errors.New("io error") + } + errBuf := make([]byte, errLen) + hostFSTake(unsafe.Pointer(unsafe.SliceData(errBuf))) + return nil, errors.New(string(errBuf)) +} + +type wasmFileInfo struct { + name string + size int64 + mode fs.FileMode + modTime time.Time +} + +func (fi *wasmFileInfo) Name() string { return fi.name } +func (fi *wasmFileInfo) Size() int64 { return fi.size } +func (fi *wasmFileInfo) Mode() fs.FileMode { return fi.mode } +func (fi *wasmFileInfo) ModTime() time.Time { return fi.modTime } +func (fi *wasmFileInfo) IsDir() bool { return fi.mode.IsDir() } +func (fi *wasmFileInfo) Sys() any { return nil } + +type wasmHostFS struct { + caseSensitive bool +} + +func (w *wasmHostFS) CaseSensitivity() tspath.CaseSensitivity { + if w.caseSensitive { + return tspath.CaseSensitive + } + return tspath.CaseInsensitive +} + +func (w *wasmHostFS) Stat(path tspath.RootedPath) vfs.FileInfo { + p := path.AsString() + out, err := hostCall(opStat, p) + if err != nil || len(out) == 0 { + return nil + } + text := unsafe.String(unsafe.SliceData(out), len(out)) + s1 := strings.IndexByte(text, ' ') + if s1 < 0 { + return nil + } + kind := text[:s1] + rest := text[s1+1:] + s2 := strings.IndexByte(rest, ' ') + var sizeStr, mtimeStr string + if s2 >= 0 { + sizeStr = rest[:s2] + mtimeStr = rest[s2+1:] + } else { + sizeStr = rest + } + var mode fs.FileMode + switch kind { + case "d": + mode = fs.ModeDir | 0o755 + case "f": + mode = 0o644 + default: + mode = fs.ModeIrregular + } + size, _ := strconv.ParseInt(sizeStr, 10, 64) + mtimeNs, _ := strconv.ParseInt(mtimeStr, 10, 64) + trimmed := strings.TrimRight(p, "/") + base := "/" + if trimmed != "" { + if idx := strings.LastIndexByte(trimmed, '/'); idx >= 0 { + base = trimmed[idx+1:] + } else { + base = trimmed + } + } + return &wasmFileInfo{ + name: base, + size: size, + mode: mode, + modTime: time.Unix(0, mtimeNs), + } +} + +func (w *wasmHostFS) FileExists(path tspath.RootedFilePath) bool { + fi := w.Stat(path.AsPath()) + return fi != nil && !fi.IsDir() +} + +func (w *wasmHostFS) DirectoryExists(path tspath.RootedDirectoryPath) bool { + fi := w.Stat(path.AsPath()) + return fi != nil && fi.IsDir() +} + +func (w *wasmHostFS) ReadFile(path tspath.RootedFilePath) (string, bool) { + b, err := hostCall(opRead, path.AsString()) + if err != nil { + return "", false + } + if len(b) == 0 { + return "", true + } + if len(b) >= 2 { + if b[0] == 0xFE && b[1] == 0xFF { + return decodeUTF16(b[2:], binary.BigEndian), true + } + if b[0] == 0xFF && b[1] == 0xFE { + return decodeUTF16(b[2:], binary.LittleEndian), true + } + if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF { + b = b[3:] + if len(b) == 0 { + return "", true + } + } + } + return unsafe.String(unsafe.SliceData(b), len(b)), true +} + +func decodeUTF16(b []byte, order binary.ByteOrder) string { + u16s := make([]uint16, len(b)/2) + for i := range u16s { + u16s[i] = order.Uint16(b[2*i:]) + } + return string(utf16.Decode(u16s)) +} + +func (w *wasmHostFS) GetAccessibleEntries(path tspath.RootedDirectoryPath) (result vfs.Entries) { + result.Symlinks = map[string]struct{}{} + out, err := hostCall(opReadDir, path.AsString()) + if err != nil || len(out) == 0 { + return result + } + raw := unsafe.String(unsafe.SliceData(out), len(out)) + items := strings.Split(raw, "\x00") + // Sort by entry name (after the 1-byte kind prefix) to match fs.ReadDir. + valid := items[:0] + for _, item := range items { + if len(item) > 1 { + valid = append(valid, item) + } + } + slices.SortFunc(valid, func(a, b string) int { + return strings.Compare(a[1:], b[1:]) + }) + for _, item := range valid { + kind := item[0] + name := item[1:] + switch kind { + case 'd': + result.Directories = append(result.Directories, name) + case 'f': + result.Files = append(result.Files, name) + case 'l': + if stat := w.Stat(path.ResolveFile(name).AsPath()); stat != nil { + if stat.IsDir() { + result.Directories = append(result.Directories, name) + result.Symlinks[name] = struct{}{} + } else if stat.Mode().IsRegular() { + result.Files = append(result.Files, name) + result.Symlinks[name] = struct{}{} + } + } + } + } + return result +} + +func (w *wasmHostFS) Realpath(path tspath.RootedPath) tspath.RootedPath { + out, err := hostCall(opRealpath, path.AsString()) + if err != nil || len(out) == 0 { + return path + } + return tspath.RootedPathFromAbsolute(tspath.NormalizeSlashes(unsafe.String(unsafe.SliceData(out), len(out)))) +} + +func (w *wasmHostFS) WriteFile(path tspath.RootedFilePath, data string) error { + _, err := hostCall(opWrite, path.AsString()+"\x00"+data) + return err +} + +func (w *wasmHostFS) AppendFile(path tspath.RootedFilePath, data string) error { + _, err := hostCall(opAppend, path.AsString()+"\x00"+data) + return err +} + +func (w *wasmHostFS) Remove(path tspath.RootedPath) error { + _, err := hostCall(opRemove, path.AsString()) + return err +} + +func (w *wasmHostFS) Chtimes(path tspath.RootedPath, aTime time.Time, mTime time.Time) error { + _, err := hostCall(opChtimes, fmt.Sprintf("%s\x00%d\x00%d", path.AsString(), aTime.UnixNano(), mTime.UnixNano())) + return err +} + +type wasmSys struct { + fs vfs.FS + defaultLibraryPath tspath.RootedDirectoryPath + cwd tspath.RootedDirectoryPath + writer io.Writer + tty bool + start time.Time +} + +func (s *wasmSys) SinceStart() time.Duration { return time.Since(s.start) } +func (s *wasmSys) Now() time.Time { return time.Now() } +func (s *wasmSys) FS() vfs.FS { return s.fs } +func (s *wasmSys) DefaultLibraryPath() tspath.RootedDirectoryPath { return s.defaultLibraryPath } +func (s *wasmSys) GetCurrentDirectory() tspath.RootedDirectoryPath { return s.cwd } +func (s *wasmSys) Writer() io.Writer { return s.writer } +func (s *wasmSys) ErrorWriter() io.Writer { return os.Stderr } +func (s *wasmSys) WriteOutputIsTTY() bool { return s.tty } +func (s *wasmSys) GetWidthOfTerminal() int { return 0 } +func (s *wasmSys) GetEnvironmentVariable(name string) (string, bool) { + return os.LookupEnv(name) +} + +func (s *wasmSys) Spawn(command []string, dir string, stderr io.Writer) (io.ReadWriteCloser, error) { + return nil, errors.New("spawn not supported on wasm") +} + +func main() {} diff --git a/tsc/internal/api/enum_values_generated.go b/tsc/internal/api/enum_values_generated.go index f90d2892104c0..299ccc05fc049 100644 --- a/tsc/internal/api/enum_values_generated.go +++ b/tsc/internal/api/enum_values_generated.go @@ -59,6 +59,7 @@ func main() { "ConstEnumOnlyModule": toInt32(ast.SymbolFlagsConstEnumOnlyModule), "ReplaceableByMethod": toInt32(ast.SymbolFlagsReplaceableByMethod), "GlobalLookup": toInt32(ast.SymbolFlagsGlobalLookup), + "NameProxy": toInt32(ast.SymbolFlagsNameProxy), "All": toInt32(ast.SymbolFlagsAll), "Enum": toInt32(ast.SymbolFlagsEnum), "Variable": toInt32(ast.SymbolFlagsVariable), @@ -244,6 +245,8 @@ func main() { "IdenticalBaseTypeCalculated": toInt32(checker.ObjectFlagsIdenticalBaseTypeCalculated), "IdenticalBaseTypeExists": toInt32(checker.ObjectFlagsIdenticalBaseTypeExists), "FromTypeNode": toInt32(checker.ObjectFlagsFromTypeNode), + "HasGenericArgumentsComputed": toInt32(checker.ObjectFlagsHasGenericArgumentsComputed), + "HasGenericArguments": toInt32(checker.ObjectFlagsHasGenericArguments), "IsGenericTypeComputed": toInt32(checker.ObjectFlagsIsGenericTypeComputed), "IsGenericObjectType": toInt32(checker.ObjectFlagsIsGenericObjectType), "IsGenericIndexType": toInt32(checker.ObjectFlagsIsGenericIndexType), @@ -750,6 +753,7 @@ func main() { "PossiblyContainsDeprecatedTag": toInt32(ast.NodeFlagsPossiblyContainsDeprecatedTag), "Unreachable": toInt32(ast.NodeFlagsUnreachable), "ReparserTransformedLiteral": toInt32(ast.NodeFlagsReparserTransformedLiteral), + "CondFlowClean": toInt32(ast.NodeFlagsCondFlowClean), "BlockScoped": toInt32(ast.NodeFlagsBlockScoped), "Constant": toInt32(ast.NodeFlagsConstant), "AwaitUsing": toInt32(ast.NodeFlagsAwaitUsing), diff --git a/tsc/internal/api/proto.go b/tsc/internal/api/proto.go index c03e7ff85ed46..47b04cb15faec 100644 --- a/tsc/internal/api/proto.go +++ b/tsc/internal/api/proto.go @@ -449,7 +449,9 @@ type CreateSnapshotParams struct { FileNotifications *FileNotifications `json:"fileNotifications,omitempty"` // FileSystem supplies file contents and directory listings for the new snapshot. // A full filesystem is canonical and total. A filesystem layer is checked - // before falling back to the base snapshot or host filesystem. + // before falling back to the base snapshot or host filesystem. A snapshot + // filesystem overlays retained request entries from another active snapshot, + // then applies an optional layer or full filesystem. FileSystem *requestfilesystem.RequestFileSystem `json:"fileSystem,omitempty"` } diff --git a/tsc/internal/api/requestfilesystem/filechanges.go b/tsc/internal/api/requestfilesystem/filechanges.go index 94f9f74a6c07a..28568887648ba 100644 --- a/tsc/internal/api/requestfilesystem/filechanges.go +++ b/tsc/internal/api/requestfilesystem/filechanges.go @@ -1,6 +1,9 @@ package requestfilesystem import ( + "maps" + "slices" + "github.com/microsoft/TypeScript/tsc/internal/collections" "github.com/microsoft/TypeScript/tsc/internal/ls/lsconv" "github.com/microsoft/TypeScript/tsc/internal/lsp/lsproto" @@ -27,6 +30,126 @@ func (s *requestFileSystem) ExpandFileChanges(summary project.FileChangeSummary) return summary } +func addRebaseFileChanges(summary *project.FileChangeSummary, source *requestFileSystem, baseFS vfs.FS, baseContents vfs.FS, rebased *requestFileSystem) { + baseRequestFS := getRequestFileSystem(baseFS) + addChange := func(path tspath.RootedPath, kind lsproto.FileChangeType) { + uri := lsconv.FileNameToDocumentURI(tspath.RootedFilePathFromPath(path)) + if kind == lsproto.FileChangeTypeDeleted { + if baseContents.FileExists(tspath.RootedFilePathFromPath(path)) || baseContents.DirectoryExists(tspath.RootedDirectoryPathFromPath(path)) { + summary.Deleted.Add(uri) + } + } else if kind == lsproto.FileChangeTypeCreated { + summary.Created.Add(uri) + } else if baseContents.FileExists(tspath.RootedFilePathFromPath(path)) { + summary.Changed.Add(uri) + } else { + summary.Created.Add(uri) + } + } + addChangeAndAliases := func(path tspath.RootedPath, kind lsproto.FileChangeType) { + addChange(path, kind) + if baseRequestFS != nil { + for _, alias := range baseRequestFS.aliasesForPath(path) { + addChange(alias, kind) + } + } + } + var addRemovedEntries func(*requestPathNode) + addRemovedEntries = func(node *requestPathNode) { + if node == nil { + return + } + switch entry := node.entry.(type) { + case *requestFile: + if !rebased.FileExists(entry.fileName) { + addChangeAndAliases(entry.fileName.AsPath(), lsproto.FileChangeTypeDeleted) + return + } + case *requestDirectory: + if !rebased.DirectoryExists(entry.directoryName) { + addChangeAndAliases(entry.directoryName.AsPath(), lsproto.FileChangeTypeDeleted) + return + } + case *requestSymlink: + if !rebased.FileExists(tspath.RootedFilePathFromPath(entry.linkName)) && !rebased.DirectoryExists(tspath.RootedDirectoryPathFromPath(entry.linkName)) { + addChangeAndAliases(entry.linkName, lsproto.FileChangeTypeDeleted) + return + } + } + for _, child := range node.children { + addRemovedEntries(child) + } + } + var visit func(*requestPathNode) + visit = func(node *requestPathNode) { + for path, child := range node.children { + _, isFile := child.entry.(*requestFile) + if child.fallback == requestFallbackMissing && child.fallbackPath != "" && !isFile { + previousFallback := requestFallbackAllowed + if baseRequestFS != nil { + _, previousFallback = baseRequestFS.paths.lookup(path) + } + if previousFallback != requestFallbackMissing { + addChangeAndAliases(child.fallbackPath, lsproto.FileChangeTypeDeleted) + } else if baseRequestFS != nil { + // A tombstone can still have explicit descendants. Reapplying + // it drops target descendants not supplied by the source. + previous, _ := baseRequestFS.paths.lookup(path) + addRemovedEntries(previous) + } + } + switch entry := child.entry.(type) { + case *requestFile: + if previous, ok := baseContents.ReadFile(entry.fileName); !ok || previous != entry.content { + addChangeAndAliases(entry.fileName.AsPath(), lsproto.FileChangeTypeChanged) + } + case *requestDirectory: + if entry.listing != nil { + // The snapshot view merges cached files into listings, even + // when omitted explicitly. Compare the supplied listings. + previous := baseFS.GetAccessibleEntries(entry.directoryName) + current := rebased.GetAccessibleEntries(entry.directoryName) + if !slices.Equal(previous.Files, current.Files) || !slices.Equal(previous.Directories, current.Directories) || !maps.Equal(previous.Symlinks, current.Symlinks) { + addChangeAndAliases(entry.directoryName.AsPath(), lsproto.FileChangeTypeDeleted) + addChangeAndAliases(entry.directoryName.AsPath(), lsproto.FileChangeTypeCreated) + } + } + if source.kind == KindFull { + previous := baseContents.GetAccessibleEntries(entry.directoryName) + for _, name := range previous.Files { + fileName := entry.directoryName.ResolveFile(name) + if !rebased.FileExists(fileName) { + addChangeAndAliases(fileName.AsPath(), lsproto.FileChangeTypeDeleted) + } + } + for _, name := range previous.Directories { + directoryName := entry.directoryName.ResolveDirectory(name) + if !rebased.DirectoryExists(directoryName) { + addChangeAndAliases(directoryName.AsPath(), lsproto.FileChangeTypeDeleted) + } + } + } + case *requestSymlink: + if baseRequestFS != nil { + previous, _ := baseRequestFS.paths.lookup(path) + if previous != nil { + if previousLink, ok := previous.entry.(*requestSymlink); ok && *previousLink == *entry { + break + } + } + } + addChangeAndAliases(entry.linkName, lsproto.FileChangeTypeDeleted) + addChangeAndAliases(entry.linkName, lsproto.FileChangeTypeCreated) + } + visit(child) + } + } + visit(source.paths) + if summary.Changed.Len()+summary.Created.Len()+summary.Deleted.Len() > 0 { + summary.IncludesWatchChangeOutsideNodeModules = true + } +} + func addFileChanges(summary *project.FileChangeSummary, request *RequestFileSystem, baseFS vfs.FS, fileSystem *requestFileSystem, currentDirectory tspath.RootedDirectoryPath) { caseSensitivity := baseFS.CaseSensitivity() baseRequestFS := getRequestFileSystem(baseFS) diff --git a/tsc/internal/api/requestfilesystem/filechanges_test.go b/tsc/internal/api/requestfilesystem/filechanges_test.go index f2b7500345131..892b04011c127 100644 --- a/tsc/internal/api/requestfilesystem/filechanges_test.go +++ b/tsc/internal/api/requestfilesystem/filechanges_test.go @@ -1,6 +1,7 @@ package requestfilesystem import ( + "strconv" "testing" "github.com/microsoft/TypeScript/tsc/internal/project" @@ -9,6 +10,105 @@ import ( "gotest.tools/v3/assert" ) +func TestRebaseFileChangesMatchLayerUpdate(t *testing.T) { + t.Parallel() + base, err := newRequestFileSystem(&RequestFileSystem{ + Kind: KindFull, + Files: map[string]string{ + "/removed/nested/file.ts": "removed", + "/replaced.ts": "old", + "/dir/old.ts": "old listing", + "/target/old.ts": "old target", + }, + Symlinks: map[string]RequestSymlink{ + "/alias": {Target: "/removed"}, + "/link": {Target: "/target/old.ts"}, + }, + }, vfstest.FromMap(map[string]string{}, tspath.CaseSensitive), "/") + assert.NilError(t, err) + for name, params := range map[string]*RequestFileSystem{ + "files and tombstones": { + Kind: KindLayer, + Files: map[string]string{"/replaced.ts": "new", "/created.ts": "created"}, + RemovedPaths: []string{"/removed", "/missing", "/replaced.ts"}, + }, + "listings and symlinks": { + Kind: KindLayer, + Directories: map[string]RequestDirectoryEntries{"/dir": {}}, + Symlinks: map[string]RequestSymlink{"/link": {Target: "/elsewhere"}, "/new": {Target: "/host", Host: true}}, + }, + } { + t.Run(name, func(t *testing.T) { + t.Parallel() + source, sourceErr := newRequestFileSystem(params, vfstest.FromMap(map[string]string{}, tspath.CaseSensitive), "/") + assert.NilError(t, sourceErr) + var expected, actual project.FileChangeSummary + _, updateErr := NewForUpdate(params, base, "/", &expected) + assert.NilError(t, updateErr) + Rebase(source, base, base, &actual) + assert.DeepEqual(t, actual, expected) + }) + } +} + +func TestRebaseFileChangesPreserveTombstoneCasing(t *testing.T) { + t.Parallel() + host := vfstest.FromMap(map[string]string{"C:/Repo/Removed/index.ts": "old"}, tspath.CaseInsensitive) + source, err := newRequestFileSystem(&RequestFileSystem{ + Kind: KindLayer, + RemovedPaths: []string{"C:/Repo/Removed"}, + }, host, "C:/Repo") + assert.NilError(t, err) + var summary project.FileChangeSummary + Rebase(source, host, host, &summary) + assert.Assert(t, summary.Deleted.Has("file:///c%3A/Repo/Removed")) + assert.Equal(t, summary.Deleted.Len(), 1) +} + +func TestRebaseFileChangesUnderMatchingTombstones(t *testing.T) { + t.Parallel() + for _, preserved := range []int{0, 1, 2} { + t.Run(strconv.Itoa(preserved), func(t *testing.T) { + t.Parallel() + host := vfstest.FromMap(map[string]string{}, tspath.CaseSensitive) + base, err := newRequestFileSystem(&RequestFileSystem{ + Kind: KindLayer, + Files: map[string]string{ + "/removed/keep.ts": "keep", + "/removed/extra/file.ts": "extra", + }, + RemovedPaths: []string{"/removed"}, + Symlinks: map[string]RequestSymlink{"/alias": {Target: "/removed"}}, + }, host, "/") + assert.NilError(t, err) + files := map[string]string{} + if preserved >= 1 { + files["/removed/keep.ts"] = "keep" + } + if preserved == 2 { + files["/removed/extra/file.ts"] = "extra" + } + source, err := newRequestFileSystem(&RequestFileSystem{ + Kind: KindLayer, Files: files, RemovedPaths: []string{"/removed"}, + }, host, "/") + assert.NilError(t, err) + var summary project.FileChangeSummary + Rebase(source, base, base, &summary) + var expected project.FileChangeSummary + switch preserved { + case 0: + expected.Deleted.Add("file:///removed") + expected.Deleted.Add("file:///alias") + case 1: + expected.Deleted.Add("file:///removed/extra") + expected.Deleted.Add("file:///alias/extra") + } + expected.IncludesWatchChangeOutsideNodeModules = preserved != 2 + assert.DeepEqual(t, summary, expected) + }) + } +} + func TestFileChangesIncludeDirectoryTombstones(t *testing.T) { t.Parallel() diff --git a/tsc/internal/api/requestfilesystem/pathtree.go b/tsc/internal/api/requestfilesystem/pathtree.go index 9fdc43185cf66..8b5cf492b4303 100644 --- a/tsc/internal/api/requestfilesystem/pathtree.go +++ b/tsc/internal/api/requestfilesystem/pathtree.go @@ -70,10 +70,11 @@ var ( ) type requestPathNode struct { - entry requestEntry - fallback requestFallback - children map[tspath.PathKey]*requestPathNode - hasSymlinks bool + entry requestEntry + fallback requestFallback + fallbackPath tspath.RootedPath + children map[tspath.PathKey]*requestPathNode + hasSymlinks bool } func (node *requestPathNode) replacesSubtree() bool { @@ -185,6 +186,7 @@ func composeRequestPaths(base *requestPathNode, overlay *requestPathNode, fallba result.children = maps.Clone(result.children) if overlay.fallback != requestFallbackInherit || overlay.replacesSubtree() { result.fallback = fallback + result.fallbackPath = overlay.fallbackPath } previousDirectory, _ := result.entry.(*requestDirectory) overlayDirectory, _ := overlay.entry.(*requestDirectory) diff --git a/tsc/internal/api/requestfilesystem/rebase_test.go b/tsc/internal/api/requestfilesystem/rebase_test.go new file mode 100644 index 0000000000000..1e724e3cf4821 --- /dev/null +++ b/tsc/internal/api/requestfilesystem/rebase_test.go @@ -0,0 +1,73 @@ +package requestfilesystem + +import ( + "reflect" + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/project" + "github.com/microsoft/TypeScript/tsc/internal/tspath" + "github.com/microsoft/TypeScript/tsc/internal/vfs" + "github.com/microsoft/TypeScript/tsc/internal/vfs/vfstest" + "gotest.tools/v3/assert" +) + +func TestRebaseDoesNotMutateInputs(t *testing.T) { + t.Parallel() + for _, sourceKind := range []Kind{KindLayer, KindFull} { + for _, targetKind := range []Kind{"host", KindLayer, KindFull} { + t.Run(string(sourceKind)+"/"+string(targetKind), func(t *testing.T) { + t.Parallel() + sourceHost := vfstest.FromMap(map[string]string{"/host.ts": "old host"}, tspath.CaseSensitive) + targetHost := vfstest.FromMap(map[string]string{"/host.ts": "new host"}, tspath.CaseSensitive) + sourceParams := &RequestFileSystem{ + Kind: sourceKind, + Files: map[string]string{ + "/dir/source.ts": "source", + "/dir/shared.ts": "source shared", + }, + Symlinks: map[string]RequestSymlink{"/alias.ts": {Target: "/dir/shared.ts"}}, + RemovedPaths: []string{"/dir/removed"}, + } + source, err := newRequestFileSystem(sourceParams, sourceHost, "/") + assert.NilError(t, err) + expectedSource, err := newRequestFileSystem(sourceParams, sourceHost, "/") + assert.NilError(t, err) + var target vfs.FS = targetHost + var expectedTarget vfs.FS = targetHost + if targetKind != "host" { + targetParams := &RequestFileSystem{ + Kind: targetKind, + Files: map[string]string{ + "/dir/target.ts": "target", + "/dir/shared.ts": "target shared", + "/dir/removed/nested.ts": "removed", + }, + Directories: map[string]RequestDirectoryEntries{ + "/dir": {Files: []string{"target.ts", "shared.ts"}, Directories: []string{"removed"}}, + }, + } + target, err = newRequestFileSystem(targetParams, targetHost, "/") + assert.NilError(t, err) + expectedTarget, err = newRequestFileSystem(targetParams, targetHost, "/") + assert.NilError(t, err) + } + var fileChanges project.FileChangeSummary + rebased := Rebase(source, target, target, &fileChanges) + assert.Assert(t, rebased != source && rebased != target) + content, ok := rebased.ReadFile("/alias.ts") + assert.Assert(t, ok) + assert.Equal(t, content, "source shared") + _, ok = rebased.ReadFile("/dir/removed/nested.ts") + assert.Assert(t, !ok) + assert.Assert(t, rebased.FileExists("/dir/source.ts")) + assert.Equal(t, rebased.FileExists("/dir/target.ts"), sourceKind == KindLayer && targetKind != "host") + _ = rebased.GetAccessibleEntries("/dir") + second := Rebase(source, source, source, &fileChanges) + assert.Assert(t, second != source) + _ = second.GetAccessibleEntries("/dir") + assert.Assert(t, reflect.DeepEqual(source, expectedSource), "source filesystem was mutated") + assert.Assert(t, reflect.DeepEqual(target, expectedTarget), "target filesystem was mutated") + }) + } + } +} diff --git a/tsc/internal/api/requestfilesystem/requestfilesystem.go b/tsc/internal/api/requestfilesystem/requestfilesystem.go index 6f52442f7b65b..6990c96e5ff76 100644 --- a/tsc/internal/api/requestfilesystem/requestfilesystem.go +++ b/tsc/internal/api/requestfilesystem/requestfilesystem.go @@ -19,6 +19,8 @@ const ( KindFull Kind = "full" // KindLayer checks the supplied filesystem before falling back to the host. KindLayer Kind = "layer" + // KindSnapshot overlays a retained snapshot's request entries before applying an optional filesystem. + KindSnapshot Kind = "snapshot" ) // RequestDirectoryEntries is a cached directory listing. Entry names are @@ -42,6 +44,10 @@ type RequestSymlink struct { // for a request that creates a snapshot. type RequestFileSystem struct { Kind Kind `json:"kind"` + // Snapshot identifies the retained snapshot supplying request entries for KindSnapshot. + Snapshot uint64 `json:"snapshot,omitempty"` + // FileSystem is an optional layer or full filesystem applied on top of KindSnapshot. + FileSystem *RequestFileSystem `json:"fileSystem,omitempty"` // Files maps file names to their complete contents. Files map[string]string `json:"files" nonnil:"true"` // Directories maps directory names to complete listing results. Directory @@ -54,6 +60,11 @@ type RequestFileSystem struct { RemovedPaths []string `json:"removedPaths,omitempty"` } +// ReplacesFileSystem reports whether the request includes an explicit full replacement. +func (r *RequestFileSystem) ReplacesFileSystem() bool { + return r != nil && (r.Kind == KindFull || r.Kind == KindSnapshot && r.FileSystem != nil && r.FileSystem.Kind == KindFull) +} + // requestFileSystem is either a full filesystem or a layer over the session // host filesystem. Its base is always the host, which may be a callback filesystem; // inherited request entries are compacted into paths. @@ -119,6 +130,42 @@ func HasFullFileSystem(fileSystem vfs.FS) bool { return requestFileSystem != nil && requestFileSystem.kind == KindFull } +// Rebase reapplies fileSystem's request entries over base. baseContents is the +// target snapshot's cached/editor view, used only to report changes relative to +// that snapshot; it must not become part of the composed filesystem. +func Rebase(fileSystem vfs.FS, base vfs.FS, baseContents vfs.FS, fileChanges *project.FileChangeSummary) vfs.FS { + source := getRequestFileSystem(fileSystem) + if source == nil { + return base + } + paths := source.paths + kind := source.kind + baseFileSystem := base + if target := getRequestFileSystem(base); target != nil { + base = target.base + if source.kind == KindLayer { + paths = composeRequestPaths(target.paths, source.paths, requestFallbackAllowed, source.caseSensitivity) + kind = target.kind + } + } + result := &requestFileSystem{ + kind: kind, + base: base, + currentDirectory: source.currentDirectory, + caseSensitivity: source.caseSensitivity, + paths: paths, + } + if source != getRequestFileSystem(baseFileSystem) { + addRebaseFileChanges(fileChanges, source, baseFileSystem, baseContents, result) + // Listings need not expose every readable entry, so only a self-rebase can + // establish that a full replacement preserves all of the target's paths. + if source.kind == KindFull { + fileChanges.InvalidateAll = true + } + } + return result +} + func newRequestFileSystemWorker(params *RequestFileSystem, base vfs.FS, currentDirectory tspath.RootedDirectoryPath) (*requestFileSystem, error) { if params.Kind != KindFull && params.Kind != KindLayer { return nil, fmt.Errorf("unknown request filesystem kind %q", params.Kind) @@ -198,7 +245,10 @@ func newRequestFileSystemWorker(params *RequestFileSystem, base vfs.FS, currentD result.registerDirectory(directoryName) } for _, path := range params.RemovedPaths { - result.paths.ensure(result.caseSensitivity.PathKey(result.toAbsolutePath(path))).fallback = requestFallbackMissing + absolutePath := result.toAbsolutePath(path) + node := result.paths.ensure(result.caseSensitivity.PathKey(absolutePath)) + node.fallback = requestFallbackMissing + node.fallbackPath = absolutePath } result.paths = composeRequestPaths(nil, result.paths, requestFallbackAllowed, result.caseSensitivity) return &result, nil diff --git a/tsc/internal/api/session.go b/tsc/internal/api/session.go index c88ef9a1b3bee..a434123b2b54d 100644 --- a/tsc/internal/api/session.go +++ b/tsc/internal/api/session.go @@ -70,7 +70,7 @@ func getSourceFileSymbolIndex(sourceFile *ast.SourceFile) map[SymbolID]*ast.Symb addSymbol(symbol.Parent()) addSymbol(symbol.ExportSymbol()) for _, table := range []ast.SymbolTable{symbol.Members(), symbol.Exports()} { - for _, child := range table { + for _, child := range table.All() { addSymbol(child) } } @@ -81,11 +81,11 @@ func getSourceFileSymbolIndex(sourceFile *ast.SourceFile) map[SymbolID]*ast.Symb } addSymbol(node.Symbol()) addSymbol(node.LocalSymbol()) - for _, symbol := range node.Locals() { + for _, symbol := range node.Locals().All() { addSymbol(symbol) } } - for _, symbol := range file.GlobalExports { + for _, symbol := range file.GlobalExports.All() { addSymbol(symbol) } for _, module := range file.PatternAmbientModules { @@ -1428,13 +1428,13 @@ func (s *Session) handleCreateSnapshot(ctx context.Context, params *CreateSnapsh fileChanges := s.toFileChangeSummary(params.FileNotifications) var snapshotFileSystem vfs.FS if params.FileSystem != nil { - fileSystem, fileSystemErr := requestfilesystem.NewForUpdate(params.FileSystem, s.FS(), s.GetCurrentDirectory(), &fileChanges) + fileSystem, fileSystemErr := s.createRequestFileSystem(params.FileSystem, s.FS(), s.FS(), &fileChanges) if fileSystemErr != nil { return nil, fmt.Errorf("%w: %w", ErrClientError, fileSystemErr) } snapshotFileSystem = fileSystem apiRequest.FileSystem = fileSystem - apiRequest.ReplaceFileSystem = params.FileSystem.Kind == requestfilesystem.KindFull + apiRequest.ReplaceFileSystem = params.FileSystem.ReplacesFileSystem() } root := s.snapshotHost.NewRootSnapshot() snapshot, err := s.snapshotHost.CloneSnapshot(ctx, root, fileChanges, apiRequest) @@ -1484,7 +1484,7 @@ func (s *Session) handleUpdateSnapshot(ctx context.Context, params *UpdateSnapsh if baseFileSystem == nil { baseFileSystem = s.FS() } - fileSystem, fileSystemErr := requestfilesystem.NewForUpdate(changes.FileSystem, baseFileSystem, s.GetCurrentDirectory(), &fileChanges) + fileSystem, fileSystemErr := s.createRequestFileSystem(changes.FileSystem, baseFileSystem, baseSD.snapshot.FS(), &fileChanges) if fileSystemErr != nil { return nil, fmt.Errorf("%w: %w", ErrClientError, fileSystemErr) } @@ -1492,7 +1492,7 @@ func (s *Session) handleUpdateSnapshot(ctx context.Context, params *UpdateSnapsh } if snapshotFileSystem != nil { apiRequest.FileSystem = snapshotFileSystem - apiRequest.ReplaceFileSystem = changes.FileSystem != nil && changes.FileSystem.Kind == requestfilesystem.KindFull + apiRequest.ReplaceFileSystem = changes.FileSystem.ReplacesFileSystem() } snapshot, err := s.snapshotHost.CloneSnapshot(ctx, baseSD.snapshot, fileChanges, apiRequest) if err != nil { @@ -1513,6 +1513,32 @@ func (s *Session) handleUpdateSnapshot(ctx context.Context, params *UpdateSnapsh return response, nil } +func (s *Session) createRequestFileSystem(params *requestfilesystem.RequestFileSystem, base vfs.FS, baseContents vfs.FS, fileChanges *project.FileChangeSummary) (vfs.FS, error) { + if params.Kind == requestfilesystem.KindSnapshot { + if len(params.Files) != 0 || len(params.Directories) != 0 || len(params.Symlinks) != 0 || len(params.RemovedPaths) != 0 { + return nil, fmt.Errorf("%w: snapshot filesystem entries must be supplied in fileSystem", ErrClientError) + } + if params.FileSystem != nil && params.FileSystem.Kind != requestfilesystem.KindLayer && params.FileSystem.Kind != requestfilesystem.KindFull { + return nil, fmt.Errorf("%w: only a layer or full filesystem can be applied on top of a snapshot filesystem", ErrClientError) + } + snapshotID := SnapshotID(params.Snapshot) + source, err := s.retainSnapshotData(snapshotID) + if err != nil { + return nil, err + } + defer func() { _ = s.releaseSnapshot(snapshotID) }() + base = requestfilesystem.Rebase(source.fileSystem, base, baseContents, fileChanges) + params = params.FileSystem + if params == nil { + return base, nil + } + } + if params.Snapshot != 0 || params.FileSystem != nil { + return nil, fmt.Errorf("%w: snapshot and fileSystem are only valid for a snapshot filesystem", ErrClientError) + } + return requestfilesystem.NewForUpdate(params, base, s.GetCurrentDirectory(), fileChanges) +} + func (s *Session) toAPISnapshotRequest(ctx context.Context, changes *SnapshotRequestChangesParams) (*project.APISnapshotRequest, error) { apiRequest := &project.APISnapshotRequest{} @@ -3451,11 +3477,11 @@ func (s *Session) resolveSymbolTablePropertyOfSymbol(ctx context.Context, params defer release() symbolTable := getter(symbol) - if len(symbolTable) == 0 { + if symbolTable.Len() == 0 { return nil, nil } - if len(symbolTable) == 1 { - for _, sub := range symbolTable { + if symbolTable.Len() == 1 { + for _, sub := range symbolTable.All() { if sd == nil { return []*SymbolResponse{newFileSymbolResponse(sub)}, nil } @@ -3463,8 +3489,8 @@ func (s *Session) resolveSymbolTablePropertyOfSymbol(ctx context.Context, params } } - symbols := make([]*ast.Symbol, 0, len(symbolTable)) - for _, sub := range symbolTable { + symbols := make([]*ast.Symbol, 0, symbolTable.Len()) + for _, sub := range symbolTable.All() { symbols = append(symbols, sub) } if sd == nil { diff --git a/tsc/internal/api/session_completion_test.go b/tsc/internal/api/session_completion_test.go index ca415027f491b..4e1a217030e99 100644 --- a/tsc/internal/api/session_completion_test.go +++ b/tsc/internal/api/session_completion_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/microsoft/TypeScript/tsc/internal/api/requestfilesystem" "github.com/microsoft/TypeScript/tsc/internal/bundled" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/ls/lsutil" @@ -326,8 +327,8 @@ func TestCompletionUsesSnapshotPreferences(t *testing.T) { func TestSnapshotCreatesProgramsAndPreparesAutoImportsInOneClone(t *testing.T) { t.Parallel() - for _, update := range []bool{false, true} { - t.Run(map[bool]string{false: "create", true: "update"}[update], func(t *testing.T) { + for _, operation := range []string{"create", "update", "snapshot filesystem"} { + t.Run(operation, func(t *testing.T) { t.Parallel() defer testutil.RecoverAndFail(t, "snapshot creation panicked") const fileName = "/home/projects/p/index.ts" @@ -351,10 +352,13 @@ func TestSnapshotCreatesProgramsAndPreparesAutoImportsInOneClone(t *testing.T) { var response *CreateSnapshotResponse var err error expectedID := SnapshotID(1) - if update { + if operation != "create" { base, e := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{}) assert.NilError(t, e) expectedID = base.Snapshot + 1 + if operation == "snapshot filesystem" { + params.FileSystem = &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(base.Snapshot)} + } response, err = session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ Snapshot: base.Snapshot, Changes: params, @@ -372,6 +376,27 @@ func TestSnapshotCreatesProgramsAndPreparesAutoImportsInOneClone(t *testing.T) { } } +func TestSnapshotFileSystemRejectsUnpreparedAutoImports(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + base, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{}) + assert.NilError(t, err) + _, err = session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: base.Snapshot, + Changes: &CreateSnapshotParams{ + PrepareAutoImports: &DocumentIdentifier{FileName: "/missing.ts"}, + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(base.Snapshot)}, + }, + }) + assert.ErrorIs(t, err, ErrClientError) + assert.ErrorContains(t, err, "could not prepare auto-imports") + assert.Equal(t, len(session.snapshots), 1) + assert.Equal(t, session.snapshots[base.Snapshot].refCount, 1) +} + func TestPreparedIndependentSnapshotPreservesLSPOverlays(t *testing.T) { t.Parallel() const fileName = "/home/projects/p/index.ts" diff --git a/tsc/internal/api/session_requestfilesystem_test.go b/tsc/internal/api/session_requestfilesystem_test.go index bf0311e577215..09d68c9c7c31f 100644 --- a/tsc/internal/api/session_requestfilesystem_test.go +++ b/tsc/internal/api/session_requestfilesystem_test.go @@ -586,107 +586,121 @@ func TestSnapshotUpdateCarriesHostFileSystemWithoutOverride(t *testing.T) { func TestSnapshotFileSystemLayersPreserveIncrementalState(t *testing.T) { t.Parallel() - for _, baseKind := range []requestfilesystem.Kind{"host", requestfilesystem.KindFull, requestfilesystem.KindLayer} { - t.Run(string(baseKind), func(t *testing.T) { - t.Parallel() - files := map[string]string{ - "/a/tsconfig.json": `{ "compilerOptions": { "noLib": true }, "include": ["**/*.ts"] }`, - "/a/index.ts": `export const value = 1;`, - "/a/removed/nested.ts": `export const nested = true;`, - "/a/removed/deep/file.ts": `export const deep = true;`, - "/b/tsconfig.json": `{ "compilerOptions": { "noLib": true }, "files": ["index.ts"] }`, - "/b/index.ts": `export const unrelated = true;`, - } - hostFiles := make(map[string]any, len(files)) - for path, content := range files { - hostFiles[path] = content - } - projectSession, _ := projecttestutil.Setup(hostFiles) - defer projectSession.Close() - session := NewLSPSession(projectSession, nil) - defer session.Close() - ctx := context.Background() - params := &CreateSnapshotParams{ - OpenProjects: []DocumentIdentifier{{FileName: "/a/tsconfig.json"}, {FileName: "/b/tsconfig.json"}}, - } - if baseKind != "host" { - params.FileSystem = &requestfilesystem.RequestFileSystem{Kind: baseKind, Files: files} - } - base, err := session.handleCreateSnapshot(ctx, params) - assert.NilError(t, err) - baseSnapshot := session.snapshots[base.Snapshot].snapshot - baseProgram := baseSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() - unrelatedProgram := baseSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() - unrelatedFile := baseSnapshot.GetFile("/b/index.ts") - - unchanged, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ - Snapshot: base.Snapshot, - Changes: &CreateSnapshotParams{ - EnsurePrograms: &EnsurePrograms{All: true}, - FileSystem: &requestfilesystem.RequestFileSystem{ - Kind: requestfilesystem.KindLayer, - Files: map[string]string{"/a/index.ts": files["/a/index.ts"]}, + for _, operation := range []string{"update", "snapshot filesystem"} { + for _, baseKind := range []requestfilesystem.Kind{"host", requestfilesystem.KindFull, requestfilesystem.KindLayer} { + t.Run(operation+"/"+string(baseKind), func(t *testing.T) { + t.Parallel() + files := map[string]string{ + "/a/tsconfig.json": `{ "compilerOptions": { "noLib": true }, "include": ["**/*.ts"] }`, + "/a/index.ts": `export const value = 1;`, + "/a/removed/nested.ts": `export const nested = true;`, + "/a/removed/deep/file.ts": `export const deep = true;`, + "/b/tsconfig.json": `{ "compilerOptions": { "noLib": true }, "files": ["index.ts"] }`, + "/b/index.ts": `export const unrelated = true;`, + } + hostFiles := make(map[string]any, len(files)) + for path, content := range files { + hostFiles[path] = content + } + projectSession, _ := projecttestutil.Setup(hostFiles) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + ctx := context.Background() + updateSnapshot := func(params *UpdateSnapshotParams) (*CreateSnapshotResponse, error) { + if operation == "snapshot filesystem" { + changes := *params.Changes + changes.FileSystem = &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindSnapshot, + Snapshot: uint64(params.Snapshot), + FileSystem: changes.FileSystem, + } + return session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{Snapshot: params.Snapshot, Changes: &changes}) + } + return session.handleUpdateSnapshot(ctx, params) + } + params := &CreateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: "/a/tsconfig.json"}, {FileName: "/b/tsconfig.json"}}, + } + if baseKind != "host" { + params.FileSystem = &requestfilesystem.RequestFileSystem{Kind: baseKind, Files: files} + } + base, err := session.handleCreateSnapshot(ctx, params) + assert.NilError(t, err) + baseSnapshot := session.snapshots[base.Snapshot].snapshot + baseProgram := baseSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() + unrelatedProgram := baseSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() + unrelatedFile := baseSnapshot.GetFile("/b/index.ts") + + unchanged, err := updateSnapshot(&UpdateSnapshotParams{ + Snapshot: base.Snapshot, + Changes: &CreateSnapshotParams{ + EnsurePrograms: &EnsurePrograms{All: true}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{"/a/index.ts": files["/a/index.ts"]}, + }, }, - }, - }) - assert.NilError(t, err) - unchangedSnapshot := session.snapshots[unchanged.Snapshot].snapshot - assert.Assert(t, unchangedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() == baseProgram) - assert.Assert(t, unchangedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) - assert.Assert(t, unchangedSnapshot.GetFile("/b/index.ts") == unrelatedFile) - - const updatedText = `export const value = 2;` - updated, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ - Snapshot: unchanged.Snapshot, - Changes: &CreateSnapshotParams{ - EnsurePrograms: &EnsurePrograms{All: true}, - FileSystem: &requestfilesystem.RequestFileSystem{ - Kind: requestfilesystem.KindLayer, - Files: map[string]string{"/a/index.ts": updatedText}, + }) + assert.NilError(t, err) + unchangedSnapshot := session.snapshots[unchanged.Snapshot].snapshot + assert.Assert(t, unchangedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() == baseProgram) + assert.Assert(t, unchangedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) + assert.Assert(t, unchangedSnapshot.GetFile("/b/index.ts") == unrelatedFile) + + const updatedText = `export const value = 2;` + updated, err := updateSnapshot(&UpdateSnapshotParams{ + Snapshot: unchanged.Snapshot, + Changes: &CreateSnapshotParams{ + EnsurePrograms: &EnsurePrograms{All: true}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{"/a/index.ts": updatedText}, + }, }, - }, - }) - assert.NilError(t, err) - updatedSnapshot := session.snapshots[updated.Snapshot].snapshot - updatedProject := updatedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")) - assert.Assert(t, updatedProject.GetProgram() != baseProgram) - assert.Equal(t, updatedProject.ProgramUpdateKind, project.ProgramUpdateKindCloned) - assert.Equal(t, updatedProject.GetProgram().GetSourceFile("/a/index.ts").Text(), updatedText) - assert.Assert(t, updatedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) - assert.Assert(t, updatedSnapshot.GetFile("/b/index.ts") == unrelatedFile) - - removed, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ - Snapshot: updated.Snapshot, - Changes: &CreateSnapshotParams{ - EnsurePrograms: &EnsurePrograms{All: true}, - FileSystem: &requestfilesystem.RequestFileSystem{ - Kind: requestfilesystem.KindLayer, - RemovedPaths: []string{"/a/removed"}, + }) + assert.NilError(t, err) + updatedSnapshot := session.snapshots[updated.Snapshot].snapshot + updatedProject := updatedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")) + assert.Assert(t, updatedProject.GetProgram() != baseProgram) + assert.Equal(t, updatedProject.ProgramUpdateKind, project.ProgramUpdateKindCloned) + assert.Equal(t, updatedProject.GetProgram().GetSourceFile("/a/index.ts").Text(), updatedText) + assert.Assert(t, updatedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) + assert.Assert(t, updatedSnapshot.GetFile("/b/index.ts") == unrelatedFile) + + removed, err := updateSnapshot(&UpdateSnapshotParams{ + Snapshot: updated.Snapshot, + Changes: &CreateSnapshotParams{ + EnsurePrograms: &EnsurePrograms{All: true}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + RemovedPaths: []string{"/a/removed"}, + }, }, - }, + }) + assert.NilError(t, err) + removedSnapshot := session.snapshots[removed.Snapshot].snapshot + removedProgram := removedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() + for _, path := range []string{"/a/removed/nested.ts", "/a/removed/deep/file.ts"} { + fileName := tspath.RootedFilePathFromAbsolute(path) + assert.Assert(t, removedProgram.GetSourceFile(fileName) == nil, path) + assert.Assert(t, removedSnapshot.GetFile(fileName) == nil, path) + assert.Assert(t, baseProgram.GetSourceFile(fileName) != nil, path) + } + assert.Assert(t, removedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) + assert.Assert(t, removedSnapshot.GetFile("/b/index.ts") == unrelatedFile) + + // A request without a base snapshot returns to the host, so the old + // layer's changed contents and directory tombstones must not survive. + restored, err := session.handleCreateSnapshot(ctx, &CreateSnapshotParams{SnapshotRequestChangesParams: params.SnapshotRequestChangesParams}) + assert.NilError(t, err) + restoredSnapshot := session.snapshots[restored.Snapshot].snapshot + assert.Assert(t, !restoredSnapshot.HasFileSystemOverride()) + restoredProgram := restoredSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() + assert.Equal(t, restoredProgram.GetSourceFile("/a/index.ts").Text(), files["/a/index.ts"]) + assert.Assert(t, restoredProgram.GetSourceFile("/a/removed/deep/file.ts") != nil) }) - assert.NilError(t, err) - removedSnapshot := session.snapshots[removed.Snapshot].snapshot - removedProgram := removedSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() - for _, path := range []string{"/a/removed/nested.ts", "/a/removed/deep/file.ts"} { - fileName := tspath.RootedFilePathFromAbsolute(path) - assert.Assert(t, removedProgram.GetSourceFile(fileName) == nil, path) - assert.Assert(t, removedSnapshot.GetFile(fileName) == nil, path) - assert.Assert(t, baseProgram.GetSourceFile(fileName) != nil, path) - } - assert.Assert(t, removedSnapshot.ProjectCollection.GetProject(project.ID("/b/tsconfig.json")).GetProgram() == unrelatedProgram) - assert.Assert(t, removedSnapshot.GetFile("/b/index.ts") == unrelatedFile) - - // A request without a base snapshot returns to the host, so the old - // layer's changed contents and directory tombstones must not survive. - restored, err := session.handleCreateSnapshot(ctx, &CreateSnapshotParams{SnapshotRequestChangesParams: params.SnapshotRequestChangesParams}) - assert.NilError(t, err) - restoredSnapshot := session.snapshots[restored.Snapshot].snapshot - assert.Assert(t, !restoredSnapshot.HasFileSystemOverride()) - restoredProgram := restoredSnapshot.ProjectCollection.GetProject(project.ID("/a/tsconfig.json")).GetProgram() - assert.Equal(t, restoredProgram.GetSourceFile("/a/index.ts").Text(), files["/a/index.ts"]) - assert.Assert(t, restoredProgram.GetSourceFile("/a/removed/deep/file.ts") != nil) - }) + } } } diff --git a/tsc/internal/api/session_snapshot_filesystem_changes_test.go b/tsc/internal/api/session_snapshot_filesystem_changes_test.go new file mode 100644 index 0000000000000..66796fe730391 --- /dev/null +++ b/tsc/internal/api/session_snapshot_filesystem_changes_test.go @@ -0,0 +1,476 @@ +package api + +import ( + "fmt" + "slices" + "testing" + + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/microsoft/TypeScript/tsc/internal/api/requestfilesystem" + "github.com/microsoft/TypeScript/tsc/internal/core" + "github.com/microsoft/TypeScript/tsc/internal/lsp/lsproto" + "github.com/microsoft/TypeScript/tsc/internal/project" + "github.com/microsoft/TypeScript/tsc/internal/testutil/projecttestutil" + "github.com/microsoft/TypeScript/tsc/internal/tspath" + "gotest.tools/v3/assert" +) + +func TestSnapshotFileSystemRemovesExplicitTombstoneDescendants(t *testing.T) { + t.Parallel() + for _, kind := range []requestfilesystem.Kind{requestfilesystem.KindLayer, requestfilesystem.KindFull} { + t.Run(string(kind), func(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, RemovedPaths: []string{"/removed"}}, + }) + assert.NilError(t, err) + target, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: kind, + Files: map[string]string{ + "/tsconfig.json": `{"compilerOptions":{"noLib":true},"include":["**/*.ts"]}`, + "/index.ts": "export {};", + "/removed/keep.ts": "export const keep = true;", + "/removed/nested/keep.ts": "export const nested = true;", + }, + RemovedPaths: []string{"/removed"}, + }, + }) + assert.NilError(t, err) + targetSnapshot := session.snapshots[target.Snapshot].snapshot + targetProgram := targetSnapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() + assert.Assert(t, targetProgram.GetSourceFile("/removed/keep.ts") != nil) + self, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(target.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + assert.Assert(t, session.snapshots[self.Snapshot].snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() == targetProgram) + + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + snapshot := session.snapshots[rebased.Snapshot].snapshot + program := snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() + for _, fileName := range []tspath.RootedFilePath{"/removed/keep.ts", "/removed/nested/keep.ts"} { + assert.Assert(t, !snapshot.FileSystem().FileExists(fileName)) + assert.Assert(t, snapshot.GetFile(fileName) == nil) + assert.Assert(t, program.GetSourceFile(fileName) == nil) + assert.Assert(t, targetProgram.GetSourceFile(fileName) != nil) + } + }) + } +} + +func TestSnapshotFullFileSystemRemovesUnlistedFiles(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + files := map[string]string{ + "/tsconfig.json": `{"compilerOptions":{"noLib":true},"files":["index.ts","hidden.ts"]}`, + "/index.ts": "export {};", + } + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindFull, Files: files}, + }) + assert.NilError(t, err) + target, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindFull, + Files: map[string]string{ + "/tsconfig.json": files["/tsconfig.json"], + "/index.ts": files["/index.ts"], + "/hidden.ts": "export const hidden = true;", + }, + Directories: map[string]requestfilesystem.RequestDirectoryEntries{ + "/": {Files: []string{"index.ts", "tsconfig.json"}}, + }, + }, + }) + assert.NilError(t, err) + assert.Assert(t, session.snapshots[target.Snapshot].snapshot.GetFile("/hidden.ts") != nil) + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + snapshot := session.snapshots[rebased.Snapshot].snapshot + assert.Assert(t, !snapshot.FileSystem().FileExists("/hidden.ts")) + assert.Assert(t, snapshot.GetFile("/hidden.ts") == nil) + assert.Assert(t, snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram().GetSourceFile("/hidden.ts") == nil) +} + +func TestSnapshotFileSystemComparesTargetSnapshotContents(t *testing.T) { + t.Parallel() + for _, editor := range []bool{false, true} { + for _, layeredTarget := range []bool{false, true} { + t.Run(fmt.Sprintf("editor=%v/layer=%v", editor, layeredTarget), func(t *testing.T) { + t.Parallel() + const oldText = "export const value = 1;" + const newText = "export const value = 2;" + projectSession, _ := projecttestutil.Setup(map[string]any{ + "/tsconfig.json": `{"compilerOptions":{"noLib":true},"files":["index.ts"]}`, + "/index.ts": oldText, + }) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + expected := newText + if editor { + expected = oldText + projectSession.DidOpenFile(t.Context(), "file:///index.ts", 1, newText, lsproto.LanguageKindTypeScript) + } + target, err := session.handleGetCurrentLanguageServerSnapshot(t.Context(), &GetCurrentLanguageServerSnapshotParams{ + Changes: &LanguageServerSnapshotChanges{OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}}}, + }) + assert.NilError(t, err) + if layeredTarget { + target, err = session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + } + targetProgram := session.snapshots[target.Snapshot].snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() + assert.Assert(t, targetProgram.GetSourceFile("/index.ts").Text() != expected) + if !editor { + assert.NilError(t, session.FS().WriteFile("/index.ts", newText)) + } + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, Files: map[string]string{"/index.ts": expected}}, + }) + assert.NilError(t, err) + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + snapshot := session.snapshots[rebased.Snapshot].snapshot + content, ok := snapshot.ReadFile("/index.ts") + assert.Assert(t, ok) + assert.Equal(t, content, expected) + proj := snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")) + assert.Equal(t, proj.GetProgram().GetSourceFile("/index.ts").Text(), expected) + assert.Equal(t, proj.ProgramUpdateKind, project.ProgramUpdateKindCloned) + }) + } + } +} + +func TestSnapshotFileSystemUnchangedListingPreservesIncrementalState(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + directories := map[string]requestfilesystem.RequestDirectoryEntries{ + "/": {Files: []string{"index.ts", "tsconfig.json"}}, + } + target, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}}, + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindFull, + Files: map[string]string{ + "/tsconfig.json": `{"compilerOptions":{"noLib":true},"files":["index.ts","hidden.ts"]}`, + "/index.ts": "export const value = 1;", + "/hidden.ts": "export const hidden = true;", + }, + Directories: directories, + }, + }) + assert.NilError(t, err) + targetProgram := session.snapshots[target.Snapshot].snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{"/index.ts": "export const value = 2;"}, + Directories: directories, + }, + }) + assert.NilError(t, err) + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + proj := session.snapshots[rebased.Snapshot].snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")) + assert.Assert(t, proj != nil) + assert.Equal(t, proj.ProgramUpdateKind, project.ProgramUpdateKindCloned) + assert.Equal(t, proj.GetProgram().GetSourceFile("/index.ts").Text(), "export const value = 2;") + assert.Assert(t, proj.GetProgram().GetSourceFile("/hidden.ts") == targetProgram.GetSourceFile("/hidden.ts")) +} + +func TestSnapshotFileSystemGeneratedFilesAreRelativeToTarget(t *testing.T) { + t.Parallel() + for _, targetHasGeneratedFiles := range []bool{false, true} { + t.Run(fmt.Sprintf("targetHasGeneratedFiles=%v", targetHasGeneratedFiles), func(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{ + "/tsconfig.json": `{"compilerOptions":{"noLib":true},"include":["*.ts"]}`, + "/index.ts": "export const value = 1;", + "/other/tsconfig.json": `{"compilerOptions":{"noLib":true},"files":["index.ts"]}`, + "/other/index.ts": "export {};", + }) + defer projectSession.Close() + projectSession.DidOpenFile(t.Context(), "file:///index.ts", 1, "export const value = 1;", lsproto.LanguageKindTypeScript) + session := NewLSPSession(projectSession, nil) + defer session.Close() + base, err := session.handleGetCurrentLanguageServerSnapshot(t.Context(), &GetCurrentLanguageServerSnapshotParams{ + Changes: &LanguageServerSnapshotChanges{OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}, {FileName: "/other/tsconfig.json"}}}, + }) + assert.NilError(t, err) + const count = 200 + files := make(map[string]string, count) + for i := range count { + files[fmt.Sprintf("/file%d.ngtypecheck.ts", i)] = "export {};" + } + layer := &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, Files: files} + source, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: base.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: layer, EnsurePrograms: &EnsurePrograms{All: true}}, + }) + assert.NilError(t, err) + projectSession.DidChangeFile(t.Context(), "file:///index.ts", 2, []lsproto.TextDocumentContentChangePartialOrWholeDocument{{ + WholeDocument: &lsproto.TextDocumentContentChangeWholeDocument{Text: "export const value = 2;"}, + }}) + target, err := session.handleGetCurrentLanguageServerSnapshot(t.Context(), &GetCurrentLanguageServerSnapshotParams{}) + assert.NilError(t, err) + if targetHasGeneratedFiles { + target, err = session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: layer, EnsurePrograms: &EnsurePrograms{All: true}}, + }) + assert.NilError(t, err) + } + targetSnapshot := session.snapshots[target.Snapshot].snapshot + unrelated := targetSnapshot.ProjectCollection.GetProject(project.ID("/other/tsconfig.json")).GetProgram() + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot), + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, Files: map[string]string{"/index.ts": "export const value = 3;"}, + }, + }, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + snapshot := session.snapshots[rebased.Snapshot].snapshot + proj := snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")) + expectedKind := project.ProgramUpdateKindNewFiles + if targetHasGeneratedFiles { + expectedKind = project.ProgramUpdateKindCloned + } + assert.Equal(t, proj.ProgramUpdateKind, expectedKind) + assert.Equal(t, len(proj.GetProgram().SourceFiles()), count+1) + assert.Equal(t, proj.GetProgram().GetSourceFile("/index.ts").Text(), "export const value = 3;") + assert.Assert(t, snapshot.ProjectCollection.GetProject(project.ID("/other/tsconfig.json")).GetProgram() == unrelated) + assert.DeepEqual(t, rebased.Changes, &SnapshotChanges{ + ChangedProjects: map[project.ID]*ProjectFileChanges{ + "/tsconfig.json": {ChangedFiles: []tspath.PathKey{"/index.ts"}}, + }, + }) + }) + } +} + +func TestSnapshotFileSystemPreservesFullInvalidation(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{"/index.ts": "export const value = 1;"}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + base, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + CreatePrograms: []*CreateSnapshotProgramParams{{ + RootFiles: []DocumentIdentifier{{FileName: "/index.ts"}}, + CompilerOptions: core.CompilerOptions{NoLib: core.TSTrue}, + }}, + }) + assert.NilError(t, err) + target, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{Snapshot: base.Snapshot}) + assert.NilError(t, err) + assert.NilError(t, session.FS().WriteFile("/index.ts", "export const value = 2;")) + changes := &CreateSnapshotParams{FileNotifications: &FileNotifications{InvalidateAll: true}, EnsurePrograms: &EnsurePrograms{All: true}} + updated, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{Snapshot: base.Snapshot, Changes: changes}) + assert.NilError(t, err) + updatedProgram, err := session.snapshots[updated.Snapshot].getProgram(updated.Projects[0].Handle) + assert.NilError(t, err) + assert.Equal(t, updatedProgram.GetSourceFile("/index.ts").Text(), "export const value = 2;") + for _, targetID := range []SnapshotID{base.Snapshot, target.Snapshot} { + snapshotChanges := *changes + snapshotChanges.FileSystem = &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(base.Snapshot)} + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{Snapshot: targetID, Changes: &snapshotChanges}) + assert.NilError(t, err) + program, err := session.snapshots[rebased.Snapshot].getProgram(rebased.Projects[0].Handle) + assert.NilError(t, err) + assert.Equal(t, program.GetSourceFile("/index.ts").Text(), "export const value = 2;") + } +} + +func TestSnapshotFileSystemRefreshesDependencyAutoImports(t *testing.T) { + t.Parallel() + const fileName = "/home/project/index.ts" + const dependency = "/home/project/node_modules/my-pkg/index.d.ts" + projectSession, _ := projecttestutil.Setup(map[string]any{ + "/home/project/tsconfig.json": `{ "compilerOptions": { "noLib": true } }`, + fileName: "package", + "/home/project/package.json": `{"dependencies":{"my-pkg":"1.0.0"}}`, + "/home/project/node_modules/my-pkg/package.json": `{"name":"my-pkg","version":"1.0.0","types":"index.d.ts"}`, + dependency: "export declare const packageOld: number;", + }) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + target, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: "/home/project/tsconfig.json"}}, + PrepareAutoImports: &DocumentIdentifier{FileName: fileName}, + }) + assert.NilError(t, err) + completionNames := func(snapshotID SnapshotID) []string { + t.Helper() + proj := session.snapshots[snapshotID].snapshot.GetDefaultProject("file:///home/project/index.ts") + completions, completionErr := session.handleGetCompletionsAtPosition(t.Context(), &GetCompletionsAtPositionParams{ + Snapshot: snapshotID, Project: proj.Handle(), File: DocumentIdentifier{FileName: fileName}, Position: 7, IncludeSymbol: true, + }) + assert.NilError(t, completionErr) + assert.Assert(t, completions != nil) + var names []string + for _, entry := range completions.Entries { + names = append(names, entry.Name) + } + return names + } + assert.Assert(t, slices.Contains(completionNames(target.Snapshot), "packageOld")) + source, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, Files: map[string]string{ + dependency: "export declare const packageNew: number;", + }}, + PrepareAutoImports: &DocumentIdentifier{FileName: fileName}, + }, + }) + assert.NilError(t, err) + assert.Assert(t, slices.Contains(completionNames(source.Snapshot), "packageNew")) + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + PrepareAutoImports: &DocumentIdentifier{FileName: fileName}, + }, + }) + assert.NilError(t, err) + names := completionNames(rebased.Snapshot) + assert.Assert(t, slices.Contains(names, "packageNew")) + assert.Assert(t, !slices.Contains(names, "packageOld")) +} + +func TestSelfSnapshotFileSystemMatchesUpdateProgramState(t *testing.T) { + t.Parallel() + for _, baseKind := range []requestfilesystem.Kind{"host", requestfilesystem.KindLayer, requestfilesystem.KindFull} { + t.Run(string(baseKind), func(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{ + "/index.ts": "export const value = 1;", + "/other.ts": "export const other = true;", + }) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + params := &CreateSnapshotParams{CreatePrograms: []*CreateSnapshotProgramParams{{ + RootFiles: []DocumentIdentifier{{FileName: "/index.ts"}}, CompilerOptions: core.CompilerOptions{NoLib: core.TSTrue}, + }}} + if baseKind != "host" { + params.FileSystem = &requestfilesystem.RequestFileSystem{ + Kind: baseKind, + Files: map[string]string{ + "/index.ts": "export const value = 1;", + "/other.ts": "export const other = true;", + "/masked/keep.ts": "export const keep = true;", + }, + Directories: map[string]requestfilesystem.RequestDirectoryEntries{"/": {Files: []string{"index.ts", "other.ts"}, Directories: []string{"masked"}}}, + Symlinks: map[string]requestfilesystem.RequestSymlink{"/alias.ts": {Target: "/index.ts"}}, + RemovedPaths: []string{"/masked"}, + } + } + base, err := session.handleCreateSnapshot(t.Context(), params) + assert.NilError(t, err) + programID := (*base.Operation.CreatedPrograms)[0] + for name, changes := range map[string]*CreateSnapshotParams{ + "no changes": {}, + "dirty program": { + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, Files: map[string]string{"/index.ts": "export const value = 2;"}}, + }, + "ensured program": { + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer, Files: map[string]string{"/index.ts": "export const value = 2;"}}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + "total replacement": { + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindFull, Files: map[string]string{"/index.ts": "export const value = 3;"}}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + "listing and symlink": { + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Directories: map[string]requestfilesystem.RequestDirectoryEntries{"/": {Files: []string{"index.ts"}}}, + Symlinks: map[string]requestfilesystem.RequestSymlink{"/alias.ts": {Target: "/other.ts"}}, + }, + }, + "create and open": { + CreatePrograms: []*CreateSnapshotProgramParams{{RootFiles: []DocumentIdentifier{{FileName: "/other.ts"}}, CompilerOptions: core.CompilerOptions{NoLib: core.TSTrue}}}, + OpenFiles: []DocumentIdentifier{{FileName: "/index.ts"}}, + }, + "reconfigure": { + ReconfigurePrograms: []*ReconfigureSnapshotProgramParams{{Id: programID, RootFiles: []DocumentIdentifier{{FileName: "/other.ts"}}, CompilerOptions: core.CompilerOptions{NoLib: core.TSTrue, Strict: core.TSTrue}}}, + }, + "remove": {RemovePrograms: []project.SyntheticProjectID{programID}}, + } { + t.Log(name) + updated, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{Snapshot: base.Snapshot, Changes: changes}) + assert.NilError(t, err, name) + snapshotChanges := *changes + snapshotChanges.FileSystem = &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(base.Snapshot), + FileSystem: changes.FileSystem, + } + rebased, err := session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{Snapshot: base.Snapshot, Changes: &snapshotChanges}) + assert.NilError(t, err, name) + // Projects created independently have distinct lifetime handles. + assert.DeepEqual(t, rebased.Projects, updated.Projects, cmpopts.IgnoreFields(ProjectResponse{}, "Handle")) + assert.DeepEqual(t, rebased.Changes, updated.Changes) + assert.DeepEqual(t, rebased.Operation, updated.Operation) + assert.Equal(t, session.snapshots[base.Snapshot].refCount, 1) + } + }) + } +} diff --git a/tsc/internal/api/session_snapshot_filesystem_test.go b/tsc/internal/api/session_snapshot_filesystem_test.go new file mode 100644 index 0000000000000..8d311b312243d --- /dev/null +++ b/tsc/internal/api/session_snapshot_filesystem_test.go @@ -0,0 +1,319 @@ +package api + +import ( + "context" + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/api/requestfilesystem" + "github.com/microsoft/TypeScript/tsc/internal/lsp/lsproto" + "github.com/microsoft/TypeScript/tsc/internal/project" + "github.com/microsoft/TypeScript/tsc/internal/testutil/projecttestutil" + "github.com/microsoft/TypeScript/tsc/internal/tspath" + "gotest.tools/v3/assert" +) + +func TestSnapshotFileSystem(t *testing.T) { + t.Parallel() + ctx := context.Background() + projectSession, _ := projecttestutil.Setup(map[string]any{ + "/tsconfig.json": `{ "compilerOptions": { "noLib": true }, "files": ["index.ts", "extra.ts"] }`, + "/index.ts": "export const value = 1;", + }) + defer projectSession.Close() + projectSession.DidOpenFile(ctx, "file:///index.ts", 1, "export const value = 1;", lsproto.LanguageKindTypeScript) + session := NewLSPSession(projectSession, nil) + defer session.Close() + original, err := session.handleGetCurrentLanguageServerSnapshot(ctx, &GetCurrentLanguageServerSnapshotParams{ + Changes: &LanguageServerSnapshotChanges{ + OpenProjects: []DocumentIdentifier{{FileName: "/tsconfig.json"}}, + }, + }) + assert.NilError(t, err) + extras, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: original.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{ + "/extra.ts": "export const extra = true;", + }, + RemovedPaths: []string{"/removed"}, + Symlinks: map[string]requestfilesystem.RequestSymlink{ + "/alias.ts": {Target: "/index.ts"}, + }, + }, + }, + }) + assert.NilError(t, err) + extras, err = session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: extras.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{"/second.ts": "second"}, + }, + }, + }) + assert.NilError(t, err) + projectSession.DidChangeFile(ctx, "file:///index.ts", 2, []lsproto.TextDocumentContentChangePartialOrWholeDocument{{ + WholeDocument: &lsproto.TextDocumentContentChangeWholeDocument{Text: "export const value = 2;"}, + }}) + newOriginal, err := session.handleGetCurrentLanguageServerSnapshot(ctx, &GetCurrentLanguageServerSnapshotParams{}) + assert.NilError(t, err) + target, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: newOriginal.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{ + "/extra.ts": "target extra", + "/target.ts": "target", + "/removed/nested.ts": "removed", + }, + }, + }, + }) + assert.NilError(t, err) + rebased, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(extras.Snapshot)}, + EnsurePrograms: &EnsurePrograms{All: true}, + }, + }) + assert.NilError(t, err) + assert.Assert(t, rebased.Snapshot != extras.Snapshot && rebased.Snapshot != target.Snapshot) + snapshot := session.snapshots[rebased.Snapshot].snapshot + for fileName, expected := range map[string]string{ + "/extra.ts": "export const extra = true;", + "/second.ts": "second", + "/target.ts": "target", + "/index.ts": "export const value = 2;", + "/alias.ts": "export const value = 2;", + } { + content, ok := snapshot.ReadFile(tspath.RootedFilePathFromAbsolute(fileName)) + assert.Assert(t, ok, fileName) + assert.Equal(t, content, expected, fileName) + } + _, ok := snapshot.ReadFile("/removed/nested.ts") + assert.Assert(t, !ok) + program := snapshot.ProjectCollection.GetProject(project.ID("/tsconfig.json")).GetProgram() + assert.Equal(t, program.GetSourceFile("/extra.ts").Text(), "export const extra = true;") + assert.Equal(t, program.GetSourceFile("/index.ts").Text(), "export const value = 2;") + content, ok := session.snapshots[extras.Snapshot].snapshot.ReadFile("/index.ts") + assert.Assert(t, ok) + assert.Equal(t, content, "export const value = 1;") + content, ok = session.snapshots[target.Snapshot].snapshot.ReadFile("/extra.ts") + assert.Assert(t, ok) + assert.Equal(t, content, "target extra") +} + +func TestSnapshotFileSystemKinds(t *testing.T) { + t.Parallel() + for _, sourceKind := range []requestfilesystem.Kind{"host", requestfilesystem.KindLayer, requestfilesystem.KindFull} { + for _, targetKind := range []requestfilesystem.Kind{"host", requestfilesystem.KindLayer, requestfilesystem.KindFull} { + t.Run(string(sourceKind)+"/"+string(targetKind), func(t *testing.T) { + t.Parallel() + ctx := context.Background() + projectSession, _ := projecttestutil.Setup(map[string]any{"/host.ts": "host"}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + sourceParams := &CreateSnapshotParams{} + if sourceKind != "host" { + sourceParams.FileSystem = &requestfilesystem.RequestFileSystem{ + Kind: sourceKind, + Files: map[string]string{"/source.ts": "source", "/shared.ts": "source"}, + } + } + source, err := session.handleCreateSnapshot(ctx, sourceParams) + assert.NilError(t, err) + targetParams := &CreateSnapshotParams{} + if targetKind != "host" { + targetParams.FileSystem = &requestfilesystem.RequestFileSystem{ + Kind: targetKind, + Files: map[string]string{"/target.ts": "target", "/shared.ts": "target"}, + } + } + target, err := session.handleCreateSnapshot(ctx, targetParams) + assert.NilError(t, err) + rebased, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: target.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}}, + }) + assert.NilError(t, err) + assert.Assert(t, rebased.Snapshot != source.Snapshot && rebased.Snapshot != target.Snapshot) + assert.NilError(t, session.releaseSnapshot(source.Snapshot)) + assert.NilError(t, session.releaseSnapshot(target.Snapshot)) + snapshot := session.snapshots[rebased.Snapshot].snapshot + content, ok := snapshot.ReadFile("/source.ts") + assert.Equal(t, ok, sourceKind != "host") + if ok { + assert.Equal(t, content, "source") + } + content, ok = snapshot.ReadFile("/target.ts") + assert.Equal(t, ok, sourceKind != requestfilesystem.KindFull && targetKind != "host") + if ok { + assert.Equal(t, content, "target") + } + _, ok = snapshot.ReadFile("/host.ts") + assert.Equal(t, ok, sourceKind != requestfilesystem.KindFull && targetKind != requestfilesystem.KindFull) + content, ok = snapshot.ReadFile("/shared.ts") + assert.Equal(t, ok, sourceKind != "host" || targetKind != "host") + if sourceKind != "host" { + assert.Equal(t, content, "source") + } else if targetKind != "host" { + assert.Equal(t, content, "target") + } + }) + } + } +} + +func TestSnapshotFileSystemRejectsInactiveSnapshots(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + ctx := context.Background() + snapshot, err := session.handleCreateSnapshot(ctx, &CreateSnapshotParams{}) + assert.NilError(t, err) + _, err = session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: snapshot.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: 999}}, + }) + assert.ErrorIs(t, err, ErrClientError) + _, err = session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: 999, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(snapshot.Snapshot)}}, + }) + assert.ErrorIs(t, err, ErrClientError) + assert.Equal(t, session.snapshots[snapshot.Snapshot].refCount, 1) + rebased, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: snapshot.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(snapshot.Snapshot)}}, + }) + assert.NilError(t, err) + assert.Assert(t, rebased.Snapshot != snapshot.Snapshot) + assert.Equal(t, session.snapshots[snapshot.Snapshot].refCount, 1) + assert.NilError(t, session.releaseSnapshot(snapshot.Snapshot)) + _, err = session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: rebased.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(snapshot.Snapshot)}}, + }) + assert.ErrorIs(t, err, ErrClientError) + _, err = session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + Snapshot: snapshot.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(rebased.Snapshot)}}, + }) + assert.ErrorIs(t, err, ErrClientError) +} + +func TestCreateSnapshotWithSnapshotFileSystem(t *testing.T) { + t.Parallel() + for _, kind := range []requestfilesystem.Kind{requestfilesystem.KindLayer, requestfilesystem.KindFull} { + t.Run(string(kind), func(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{"/host.ts": "host"}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindLayer, + Files: map[string]string{"/source.ts": "source", "/shared.ts": "source", "/removed/nested.ts": "removed"}, + }, + }) + assert.NilError(t, err) + cloned, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + }) + assert.NilError(t, err) + assert.Assert(t, cloned.Snapshot != source.Snapshot) + content, ok := session.snapshots[cloned.Snapshot].snapshot.ReadFile("/source.ts") + assert.Assert(t, ok) + assert.Equal(t, content, "source") + created, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{ + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot), + FileSystem: &requestfilesystem.RequestFileSystem{ + Kind: kind, + Files: map[string]string{"/extra.ts": "extra", "/shared.ts": "extra"}, + Directories: map[string]requestfilesystem.RequestDirectoryEntries{ + "/listed": {Files: []string{"entry.ts"}}, + }, + Symlinks: map[string]requestfilesystem.RequestSymlink{ + "/alias.ts": {Target: "/shared.ts"}, + }, + RemovedPaths: []string{"/removed"}, + }, + }, + }) + assert.NilError(t, err) + assert.Assert(t, created.Snapshot != source.Snapshot) + assert.NilError(t, session.releaseSnapshot(source.Snapshot)) + snapshot := session.snapshots[created.Snapshot].snapshot + for fileName, expected := range map[tspath.RootedFilePath]string{ + "/extra.ts": "extra", "/shared.ts": "extra", "/alias.ts": "extra", + } { + content, ok = snapshot.ReadFile(fileName) + assert.Assert(t, ok) + assert.Equal(t, content, expected) + } + _, ok = snapshot.ReadFile("/source.ts") + assert.Equal(t, ok, kind == requestfilesystem.KindLayer) + _, ok = snapshot.ReadFile("/host.ts") + assert.Equal(t, ok, kind == requestfilesystem.KindLayer) + _, ok = snapshot.ReadFile("/removed/nested.ts") + assert.Assert(t, !ok) + assert.Assert(t, snapshot.FileSystem().DirectoryExists("/listed")) + }) + } +} + +func TestSnapshotFileSystemRejectsInvalidDescriptors(t *testing.T) { + t.Parallel() + projectSession, _ := projecttestutil.Setup(map[string]any{}) + defer projectSession.Close() + session := NewLSPSession(projectSession, nil) + defer session.Close() + source, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{}) + assert.NilError(t, err) + released, err := session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{}) + assert.NilError(t, err) + assert.NilError(t, session.releaseSnapshot(released.Snapshot)) + for name, fileSystem := range map[string]*requestfilesystem.RequestFileSystem{ + "invalid kind": {Kind: "invalid"}, + "zero snapshot": {Kind: requestfilesystem.KindSnapshot}, + "unknown snapshot": {Kind: requestfilesystem.KindSnapshot, Snapshot: 999}, + "released snapshot": {Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(released.Snapshot)}, + "snapshot on layer": {Kind: requestfilesystem.KindLayer, Snapshot: uint64(source.Snapshot)}, + "nested filesystem on full": { + Kind: requestfilesystem.KindFull, + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindLayer}, + }, + "nested snapshot": { + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot), + FileSystem: &requestfilesystem.RequestFileSystem{Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot)}, + }, + "invalid nested kind": { + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot), + FileSystem: &requestfilesystem.RequestFileSystem{Kind: "invalid"}, + }, + "unnested entries": { + Kind: requestfilesystem.KindSnapshot, Snapshot: uint64(source.Snapshot), + Files: map[string]string{"/extra.ts": "extra"}, + }, + } { + t.Log(name) + _, err = session.handleCreateSnapshot(t.Context(), &CreateSnapshotParams{FileSystem: fileSystem}) + assert.ErrorIs(t, err, ErrClientError, name) + _, err = session.handleUpdateSnapshot(t.Context(), &UpdateSnapshotParams{ + Snapshot: source.Snapshot, + Changes: &CreateSnapshotParams{FileSystem: fileSystem}, + }) + assert.ErrorIs(t, err, ErrClientError, name) + assert.Equal(t, session.snapshots[source.Snapshot].refCount, 1) + } +} diff --git a/tsc/internal/api/session_symbolresponse_test.go b/tsc/internal/api/session_symbolresponse_test.go index 90b19bc927beb..13b67efa54332 100644 --- a/tsc/internal/api/session_symbolresponse_test.go +++ b/tsc/internal/api/session_symbolresponse_test.go @@ -86,7 +86,7 @@ func TestSymbolReferencesIdentifyOwnerWithoutDescriptor(t *testing.T) { assert.Assert(t, !strings.Contains(string(encoded), "contentHash")) // A file-owned symbol's relationships are references into the same file. - member := class.Members()["property"] + member := class.Members().Get("property") response := newFileSymbolResponse(member) assert.DeepEqual(t, response.Parent, reference) } @@ -110,6 +110,6 @@ func TestContentMappedSymbolsAreSnapshotOwned(t *testing.T) { assert.NilError(t, err) assert.Equal(t, resolved, class) - reference := newSymbolReference(class.Members()["property"]) + reference := newSymbolReference(class.Members().Get("property")) assert.Equal(t, reference.File, "") } diff --git a/tsc/internal/ast/ast.go b/tsc/internal/ast/ast.go index 48ff1a8d63d56..849a4adfa5733 100644 --- a/tsc/internal/ast/ast.go +++ b/tsc/internal/ast/ast.go @@ -31,6 +31,12 @@ type Visitor func(*Node) bool func visit(v Visitor, node *Node) bool { if node != nil { + if node.Kind == KindUnknown { + node = node.forceLazyTypeNode() + if node == nil { + return false + } + } return v(node) } return false @@ -47,6 +53,9 @@ func visitNodes(v Visitor, nodes []*Node) bool { func visitNodeList(v Visitor, nodeList *NodeList) bool { if nodeList != nil { + if unsafe.SliceData(nodeList.Nodes) == &lazySentinelElem { + return visitNodes(v, nodeList.forceLazy()) + } return visitNodes(v, nodeList.Nodes) } return false @@ -95,6 +104,11 @@ func (f *NodeFactory) TextCount() int { return f.textCount } +func (f *NodeFactory) ResetCounts() { + f.nodeCount = 0 + f.textCount = 0 +} + func (f *NodeFactory) AsNodeFactory() *NodeFactory { return f } @@ -136,15 +150,16 @@ func (list *NodeList) Pos() int { return list.Loc.Pos() } func (list *NodeList) End() int { return list.Loc.End() } func (list *NodeList) HasTrailingComma() bool { - if len(list.Nodes) == 0 { + nodes := list.Slice() + if len(nodes) == 0 { return false } - last := list.Nodes[len(list.Nodes)-1] + last := nodes[len(nodes)-1] return last.End() < list.End() } func (list *NodeList) Clone(f NodeFactoryCoercible) *NodeList { - result := f.AsNodeFactory().NewNodeList(list.Nodes) + result := f.AsNodeFactory().NewNodeList(list.Slice()) result.Loc = list.Loc return result } @@ -174,12 +189,14 @@ func (list *ModifierList) Clone(f *NodeFactory) *ModifierList { // Every concrete AST node embeds Node at offset zero. Kind identifies the // enclosing allocation, which generated accessors recover without a data pointer. +// Initialize Kind only in newNode; transformations must construct a new node +// rather than retagging one. The unsafelayout linter enforces this boundary. type Node struct { Kind Kind Flags NodeFlags Loc core.TextRange - id atomic.Uint64 + id atomic.Uint32 Parent *Node } @@ -244,32 +261,35 @@ func (n *Node) Body() *Node { return nil } +const nodeFlagsOffset = uint8(unsafe.Offsetof(Node{}.Flags)) + +var nodeTextOffset = [kindFlowReduceLabelData + 1]uint8{ + KindIdentifier: uint8(unsafe.Offsetof(Identifier{}.Text)) - nodeFlagsOffset, + KindPrivateIdentifier: uint8(unsafe.Offsetof(PrivateIdentifier{}.Text)) - nodeFlagsOffset, + KindStringLiteral: uint8(unsafe.Offsetof(StringLiteral{}.Text)) - nodeFlagsOffset, + KindNumericLiteral: uint8(unsafe.Offsetof(NumericLiteral{}.Text)) - nodeFlagsOffset, + KindBigIntLiteral: uint8(unsafe.Offsetof(BigIntLiteral{}.Text)) - nodeFlagsOffset, + KindNoSubstitutionTemplateLiteral: uint8(unsafe.Offsetof(NoSubstitutionTemplateLiteral{}.Text)) - nodeFlagsOffset, + KindTemplateHead: uint8(unsafe.Offsetof(TemplateHead{}.Text)) - nodeFlagsOffset, + KindTemplateMiddle: uint8(unsafe.Offsetof(TemplateMiddle{}.Text)) - nodeFlagsOffset, + KindTemplateTail: uint8(unsafe.Offsetof(TemplateTail{}.Text)) - nodeFlagsOffset, + KindJsxText: uint8(unsafe.Offsetof(JsxText{}.Text)) - nodeFlagsOffset, + KindRegularExpressionLiteral: uint8(unsafe.Offsetof(RegularExpressionLiteral{}.Text)) - nodeFlagsOffset, +} + func (n *Node) Text() string { + if offset := nodeTextOffset[n.Kind]; offset != 0 { + return *(*string)(unsafe.Add(unsafe.Pointer(&n.Flags), offset)) + } + return n.textSlow() +} + +func (n *Node) textSlow() string { switch n.Kind { - case KindIdentifier: - return n.AsIdentifier().Text - case KindPrivateIdentifier: - return n.AsPrivateIdentifier().Text - case KindStringLiteral: - return n.AsStringLiteral().Text - case KindNumericLiteral: - return n.AsNumericLiteral().Text - case KindBigIntLiteral: - return n.AsBigIntLiteral().Text case KindMetaProperty: return n.AsMetaProperty().Name().Text() - case KindNoSubstitutionTemplateLiteral: - return n.AsNoSubstitutionTemplateLiteral().Text - case KindTemplateHead: - return n.AsTemplateHead().Text - case KindTemplateMiddle: - return n.AsTemplateMiddle().Text - case KindTemplateTail: - return n.AsTemplateTail().Text case KindJsxNamespacedName: return n.AsJsxNamespacedName().Namespace.Text() + ":" + n.AsJsxNamespacedName().name.Text() - case KindRegularExpressionLiteral: - return n.AsRegularExpressionLiteral().Text case KindJSDocText: return strings.Join(n.AsJSDocText().text, "") case KindJSDocLink: @@ -282,80 +302,50 @@ func (n *Node) Text() string { panic(fmt.Sprintf("Unhandled case in Node.Text: %s", n.Kind)) } +var nodeExpressionOffset = [kindFlowReduceLabelData + 1]uint8{ + KindPropertyAccessExpression: uint8(unsafe.Offsetof(PropertyAccessExpression{}.Expression)), + KindElementAccessExpression: uint8(unsafe.Offsetof(ElementAccessExpression{}.Expression)), + KindParenthesizedExpression: uint8(unsafe.Offsetof(ParenthesizedExpression{}.Expression)), + KindCallExpression: uint8(unsafe.Offsetof(CallExpression{}.Expression)), + KindNewExpression: uint8(unsafe.Offsetof(NewExpression{}.Expression)), + KindExpressionWithTypeArguments: uint8(unsafe.Offsetof(ExpressionWithTypeArguments{}.Expression)), + KindComputedPropertyName: uint8(unsafe.Offsetof(ComputedPropertyName{}.Expression)), + KindNonNullExpression: uint8(unsafe.Offsetof(NonNullExpression{}.Expression)), + KindTypeAssertionExpression: uint8(unsafe.Offsetof(TypeAssertion{}.Expression)), + KindAsExpression: uint8(unsafe.Offsetof(AsExpression{}.Expression)), + KindSatisfiesExpression: uint8(unsafe.Offsetof(SatisfiesExpression{}.Expression)), + KindTypeOfExpression: uint8(unsafe.Offsetof(TypeOfExpression{}.Expression)), + KindSpreadAssignment: uint8(unsafe.Offsetof(SpreadAssignment{}.Expression)), + KindSpreadElement: uint8(unsafe.Offsetof(SpreadElement{}.Expression)), + KindTemplateSpan: uint8(unsafe.Offsetof(TemplateSpan{}.Expression)), + KindDeleteExpression: uint8(unsafe.Offsetof(DeleteExpression{}.Expression)), + KindVoidExpression: uint8(unsafe.Offsetof(VoidExpression{}.Expression)), + KindAwaitExpression: uint8(unsafe.Offsetof(AwaitExpression{}.Expression)), + KindYieldExpression: uint8(unsafe.Offsetof(YieldExpression{}.Expression)), + KindPartiallyEmittedExpression: uint8(unsafe.Offsetof(PartiallyEmittedExpression{}.Expression)), + KindIfStatement: uint8(unsafe.Offsetof(IfStatement{}.Expression)), + KindDoStatement: uint8(unsafe.Offsetof(DoStatement{}.Expression)), + KindWhileStatement: uint8(unsafe.Offsetof(WhileStatement{}.Expression)), + KindWithStatement: uint8(unsafe.Offsetof(WithStatement{}.Expression)), + KindForInStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.Expression)), + KindForOfStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.Expression)), + KindSwitchStatement: uint8(unsafe.Offsetof(SwitchStatement{}.Expression)), + KindCaseClause: uint8(unsafe.Offsetof(CaseOrDefaultClause{}.Expression)), + KindExpressionStatement: uint8(unsafe.Offsetof(ExpressionStatement{}.Expression)), + KindReturnStatement: uint8(unsafe.Offsetof(ReturnStatement{}.Expression)), + KindThrowStatement: uint8(unsafe.Offsetof(ThrowStatement{}.Expression)), + KindExternalModuleReference: uint8(unsafe.Offsetof(ExternalModuleReference{}.Expression)), + KindExportAssignment: uint8(unsafe.Offsetof(ExportAssignment{}.Expression)), + KindDecorator: uint8(unsafe.Offsetof(Decorator{}.Expression)), + KindJsxExpression: uint8(unsafe.Offsetof(JsxExpression{}.Expression)), + KindJsxSpreadAttribute: uint8(unsafe.Offsetof(JsxSpreadAttribute{}.Expression)), +} + func (n *Node) Expression() *Node { - switch n.Kind { - case KindPropertyAccessExpression: - return n.AsPropertyAccessExpression().Expression - case KindElementAccessExpression: - return n.AsElementAccessExpression().Expression - case KindParenthesizedExpression: - return n.AsParenthesizedExpression().Expression - case KindCallExpression: - return n.AsCallExpression().Expression - case KindNewExpression: - return n.AsNewExpression().Expression - case KindExpressionWithTypeArguments: - return n.AsExpressionWithTypeArguments().Expression - case KindComputedPropertyName: - return n.AsComputedPropertyName().Expression - case KindNonNullExpression: - return n.AsNonNullExpression().Expression - case KindTypeAssertionExpression: - return n.AsTypeAssertion().Expression - case KindAsExpression: - return n.AsAsExpression().Expression - case KindSatisfiesExpression: - return n.AsSatisfiesExpression().Expression - case KindTypeOfExpression: - return n.AsTypeOfExpression().Expression - case KindSpreadAssignment: - return n.AsSpreadAssignment().Expression - case KindSpreadElement: - return n.AsSpreadElement().Expression - case KindTemplateSpan: - return n.AsTemplateSpan().Expression - case KindDeleteExpression: - return n.AsDeleteExpression().Expression - case KindVoidExpression: - return n.AsVoidExpression().Expression - case KindAwaitExpression: - return n.AsAwaitExpression().Expression - case KindYieldExpression: - return n.AsYieldExpression().Expression - case KindPartiallyEmittedExpression: - return n.AsPartiallyEmittedExpression().Expression - case KindIfStatement: - return n.AsIfStatement().Expression - case KindDoStatement: - return n.AsDoStatement().Expression - case KindWhileStatement: - return n.AsWhileStatement().Expression - case KindWithStatement: - return n.AsWithStatement().Expression - case KindForInStatement, KindForOfStatement: - return n.AsForInOrOfStatement().Expression - case KindSwitchStatement: - return n.AsSwitchStatement().Expression - case KindCaseClause: - return n.AsCaseOrDefaultClause().Expression - case KindExpressionStatement: - return n.AsExpressionStatement().Expression - case KindReturnStatement: - return n.AsReturnStatement().Expression - case KindThrowStatement: - return n.AsThrowStatement().Expression - case KindExternalModuleReference: - return n.AsExternalModuleReference().Expression - case KindExportAssignment: - return n.AsExportAssignment().Expression - case KindDecorator: - return n.AsDecorator().Expression - case KindJsxExpression: - return n.AsJsxExpression().Expression - case KindJsxSpreadAttribute: - return n.AsJsxSpreadAttribute().Expression - } - panic("Unhandled case in Node.Expression: " + n.Kind.String()) + if offset := nodeExpressionOffset[n.Kind]; offset != 0 { + return *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) + } + panic("Unhandled case in Node.Expression") } func (n *Node) RawText() string { @@ -372,80 +362,11 @@ func (n *Node) RawText() string { func (m *MutableNode) SetExpression(expr *Node) { n := (*Node)(m) - switch n.Kind { - case KindPropertyAccessExpression: - n.AsPropertyAccessExpression().Expression = expr - case KindElementAccessExpression: - n.AsElementAccessExpression().Expression = expr - case KindParenthesizedExpression: - n.AsParenthesizedExpression().Expression = expr - case KindCallExpression: - n.AsCallExpression().Expression = expr - case KindNewExpression: - n.AsNewExpression().Expression = expr - case KindExpressionWithTypeArguments: - n.AsExpressionWithTypeArguments().Expression = expr - case KindComputedPropertyName: - n.AsComputedPropertyName().Expression = expr - case KindNonNullExpression: - n.AsNonNullExpression().Expression = expr - case KindTypeAssertionExpression: - n.AsTypeAssertion().Expression = expr - case KindAsExpression: - n.AsAsExpression().Expression = expr - case KindSatisfiesExpression: - n.AsSatisfiesExpression().Expression = expr - case KindTypeOfExpression: - n.AsTypeOfExpression().Expression = expr - case KindSpreadAssignment: - n.AsSpreadAssignment().Expression = expr - case KindSpreadElement: - n.AsSpreadElement().Expression = expr - case KindTemplateSpan: - n.AsTemplateSpan().Expression = expr - case KindDeleteExpression: - n.AsDeleteExpression().Expression = expr - case KindVoidExpression: - n.AsVoidExpression().Expression = expr - case KindAwaitExpression: - n.AsAwaitExpression().Expression = expr - case KindYieldExpression: - n.AsYieldExpression().Expression = expr - case KindPartiallyEmittedExpression: - n.AsPartiallyEmittedExpression().Expression = expr - case KindIfStatement: - n.AsIfStatement().Expression = expr - case KindDoStatement: - n.AsDoStatement().Expression = expr - case KindWhileStatement: - n.AsWhileStatement().Expression = expr - case KindWithStatement: - n.AsWithStatement().Expression = expr - case KindForInStatement, KindForOfStatement: - n.AsForInOrOfStatement().Expression = expr - case KindSwitchStatement: - n.AsSwitchStatement().Expression = expr - case KindCaseClause: - n.AsCaseOrDefaultClause().Expression = expr - case KindExpressionStatement: - n.AsExpressionStatement().Expression = expr - case KindReturnStatement: - n.AsReturnStatement().Expression = expr - case KindThrowStatement: - n.AsThrowStatement().Expression = expr - case KindExternalModuleReference: - n.AsExternalModuleReference().Expression = expr - case KindExportAssignment: - n.AsExportAssignment().Expression = expr - case KindDecorator: - n.AsDecorator().Expression = expr - case KindJsxExpression: - n.AsJsxExpression().Expression = expr - case KindJsxSpreadAttribute: - n.AsJsxSpreadAttribute().Expression = expr - default: - panic("Unhandled case in mutableNode.SetExpression: " + n.Kind.String()) + if offset := nodeExpressionOffset[n.Kind]; offset != 0 { + *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) = expr + return } + panic("Unhandled case in mutableNode.SetExpression") } func (n *Node) ArgumentList() *NodeList { @@ -466,26 +387,21 @@ func (n *Node) Arguments() []*Node { return nil } +var nodeTypeArgumentsOffset = [kindFlowReduceLabelData + 1]uint8{ + KindCallExpression: uint8(unsafe.Offsetof(CallExpression{}.TypeArguments)), + KindNewExpression: uint8(unsafe.Offsetof(NewExpression{}.TypeArguments)), + KindTaggedTemplateExpression: uint8(unsafe.Offsetof(TaggedTemplateExpression{}.TypeArguments)), + KindTypeReference: uint8(unsafe.Offsetof(TypeReferenceNode{}.TypeArguments)), + KindExpressionWithTypeArguments: uint8(unsafe.Offsetof(ExpressionWithTypeArguments{}.TypeArguments)), + KindImportType: uint8(unsafe.Offsetof(ImportTypeNode{}.TypeArguments)), + KindTypeQuery: uint8(unsafe.Offsetof(TypeQueryNode{}.TypeArguments)), + KindJsxOpeningElement: uint8(unsafe.Offsetof(JsxOpeningElement{}.TypeArguments)), + KindJsxSelfClosingElement: uint8(unsafe.Offsetof(JsxSelfClosingElement{}.TypeArguments)), +} + func (n *Node) TypeArgumentList() *NodeList { - switch n.Kind { - case KindCallExpression: - return n.AsCallExpression().TypeArguments - case KindNewExpression: - return n.AsNewExpression().TypeArguments - case KindTaggedTemplateExpression: - return n.AsTaggedTemplateExpression().TypeArguments - case KindTypeReference: - return n.AsTypeReferenceNode().TypeArguments - case KindExpressionWithTypeArguments: - return n.AsExpressionWithTypeArguments().TypeArguments - case KindImportType: - return n.AsImportTypeNode().TypeArguments - case KindTypeQuery: - return n.AsTypeQueryNode().TypeArguments - case KindJsxOpeningElement: - return n.AsJsxOpeningElement().TypeArguments - case KindJsxSelfClosingElement: - return n.AsJsxSelfClosingElement().TypeArguments + if offset := nodeTypeArgumentsOffset[n.Kind]; offset != 0 { + return *(**NodeList)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) } panic("Unhandled case in Node.TypeArguments") } @@ -498,23 +414,21 @@ func (n *Node) TypeArguments() []*Node { return nil } +var nodeTypeParametersOffset = [kindFlowReduceLabelData + 1]uint8{ + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.TypeParameters)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.TypeParameters)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.TypeParameters)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.TypeParameters)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.TypeParameters)), + KindJSDocTemplateTag: uint8(unsafe.Offsetof(JSDocTemplateTag{}.TypeParameters)), +} + func (n *Node) TypeParameterList() *NodeList { - switch n.Kind { - case KindClassDeclaration: - return n.AsClassDeclaration().TypeParameters - case KindClassExpression: - return n.AsClassExpression().TypeParameters - case KindInterfaceDeclaration: - return n.AsInterfaceDeclaration().TypeParameters - case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: - return n.AsTypeAliasDeclaration().TypeParameters - case KindJSDocTemplateTag: - return n.AsJSDocTemplateTag().TypeParameters - default: - funcLike := n.FunctionLikeData() - if funcLike != nil { - return funcLike.TypeParameters - } + if offset := nodeTypeParametersOffset[n.Kind]; offset != 0 { + return *(**NodeList)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) + } + if funcLike := n.FunctionLikeData(); funcLike != nil { + return funcLike.TypeParameters } panic("Unhandled case in Node.TypeParameterList") } @@ -527,44 +441,46 @@ func (n *Node) TypeParameters() []*Node { return nil } +var nodeMembersOffset = [kindFlowReduceLabelData + 1]uint8{ + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.Members)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.Members)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.Members)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.Members)), + KindTypeLiteral: uint8(unsafe.Offsetof(TypeLiteralNode{}.Members)), + KindMappedType: uint8(unsafe.Offsetof(MappedTypeNode{}.Members)), +} + func (n *Node) MemberList() *NodeList { - switch n.Kind { - case KindClassDeclaration: - return n.AsClassDeclaration().Members - case KindClassExpression: - return n.AsClassExpression().Members - case KindInterfaceDeclaration: - return n.AsInterfaceDeclaration().Members - case KindEnumDeclaration: - return n.AsEnumDeclaration().Members - case KindTypeLiteral: - return n.AsTypeLiteralNode().Members - case KindMappedType: - return n.AsMappedTypeNode().Members + if offset := nodeMembersOffset[n.Kind]; offset != 0 { + return *(**NodeList)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) } - panic("Unhandled case in Node.MemberList: " + n.Kind.String()) + panic("Unhandled case in Node.MemberList") } func (n *Node) Members() []*Node { list := n.MemberList() if list != nil { + if unsafe.SliceData(list.Nodes) == &lazySentinelElem { + return list.forceLazy() + } return list.Nodes } return nil } +var nodeStatementsOffset = [kindFlowReduceLabelData + 1]uint8{ + KindSourceFile: uint8(unsafe.Offsetof(SourceFile{}.Statements)), + KindBlock: uint8(unsafe.Offsetof(Block{}.Statements)), + KindModuleBlock: uint8(unsafe.Offsetof(ModuleBlock{}.Statements)), + KindCaseClause: uint8(unsafe.Offsetof(CaseOrDefaultClause{}.Statements)), + KindDefaultClause: uint8(unsafe.Offsetof(CaseOrDefaultClause{}.Statements)), +} + func (n *Node) StatementList() *NodeList { - switch n.Kind { - case KindSourceFile: - return n.AsSourceFile().Statements - case KindBlock: - return n.AsBlock().Statements - case KindModuleBlock: - return n.AsModuleBlock().Statements - case KindCaseClause, KindDefaultClause: - return n.AsCaseOrDefaultClause().Statements + if offset := nodeStatementsOffset[n.Kind]; offset != 0 { + return *(**NodeList)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) } - panic("Unhandled case in Node.StatementList: " + n.Kind.String()) + panic("Unhandled case in Node.StatementList") } func (n *Node) Statements() []*Node { @@ -600,180 +516,91 @@ func (n *Node) ModifierNodes() []*Node { return nil } +var nodeTypeOffset = [kindFlowReduceLabelData + 1]uint8{ + KindVariableDeclaration: uint8(unsafe.Offsetof(VariableDeclaration{}.Type)), + KindParameter: uint8(unsafe.Offsetof(ParameterDeclaration{}.Type)), + KindPropertySignature: uint8(unsafe.Offsetof(PropertySignatureDeclaration{}.Type)), + KindPropertyDeclaration: uint8(unsafe.Offsetof(PropertyDeclaration{}.Type)), + KindPropertyAssignment: uint8(unsafe.Offsetof(PropertyAssignment{}.Type)), + KindShorthandPropertyAssignment: uint8(unsafe.Offsetof(ShorthandPropertyAssignment{}.Type)), + KindTypePredicate: uint8(unsafe.Offsetof(TypePredicateNode{}.Type)), + KindParenthesizedType: uint8(unsafe.Offsetof(ParenthesizedTypeNode{}.Type)), + KindTypeOperator: uint8(unsafe.Offsetof(TypeOperatorNode{}.Type)), + KindMappedType: uint8(unsafe.Offsetof(MappedTypeNode{}.Type)), + KindTypeAssertionExpression: uint8(unsafe.Offsetof(TypeAssertion{}.Type)), + KindAsExpression: uint8(unsafe.Offsetof(AsExpression{}.Type)), + KindSatisfiesExpression: uint8(unsafe.Offsetof(SatisfiesExpression{}.Type)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.Type)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.Type)), + KindNamedTupleMember: uint8(unsafe.Offsetof(NamedTupleMember{}.Type)), + KindOptionalType: uint8(unsafe.Offsetof(OptionalTypeNode{}.Type)), + KindRestType: uint8(unsafe.Offsetof(RestTypeNode{}.Type)), + KindTemplateLiteralTypeSpan: uint8(unsafe.Offsetof(TemplateLiteralTypeSpan{}.Type)), + KindJSDocTypeExpression: uint8(unsafe.Offsetof(JSDocTypeExpression{}.Type)), + KindJSDocParameterTag: uint8(unsafe.Offsetof(JSDocParameterOrPropertyTag{}.TypeExpression)), + KindJSDocPropertyTag: uint8(unsafe.Offsetof(JSDocParameterOrPropertyTag{}.TypeExpression)), + KindJSDocNullableType: uint8(unsafe.Offsetof(JSDocNullableType{}.Type)), + KindJSDocNonNullableType: uint8(unsafe.Offsetof(JSDocNonNullableType{}.Type)), + KindJSDocOptionalType: uint8(unsafe.Offsetof(JSDocOptionalType{}.Type)), + KindExportAssignment: uint8(unsafe.Offsetof(ExportAssignment{}.Type)), + KindBinaryExpression: uint8(unsafe.Offsetof(BinaryExpression{}.Type)), +} + func (n *Node) Type() *Node { - switch n.Kind { - case KindVariableDeclaration: - return n.AsVariableDeclaration().Type - case KindParameter: - return n.AsParameterDeclaration().Type - case KindPropertySignature: - return n.AsPropertySignatureDeclaration().Type - case KindPropertyDeclaration: - return n.AsPropertyDeclaration().Type - case KindPropertyAssignment: - return n.AsPropertyAssignment().Type - case KindShorthandPropertyAssignment: - return n.AsShorthandPropertyAssignment().Type - case KindTypePredicate: - return n.AsTypePredicateNode().Type - case KindParenthesizedType: - return n.AsParenthesizedTypeNode().Type - case KindTypeOperator: - return n.AsTypeOperatorNode().Type - case KindMappedType: - return n.AsMappedTypeNode().Type - case KindTypeAssertionExpression: - return n.AsTypeAssertion().Type - case KindAsExpression: - return n.AsAsExpression().Type - case KindSatisfiesExpression: - return n.AsSatisfiesExpression().Type - case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: - return n.AsTypeAliasDeclaration().Type - case KindNamedTupleMember: - return n.AsNamedTupleMember().Type - case KindOptionalType: - return n.AsOptionalTypeNode().Type - case KindRestType: - return n.AsRestTypeNode().Type - case KindTemplateLiteralTypeSpan: - return n.AsTemplateLiteralTypeSpan().Type - case KindJSDocTypeExpression: - return n.AsJSDocTypeExpression().Type - case KindJSDocParameterTag, KindJSDocPropertyTag: - return n.AsJSDocParameterOrPropertyTag().TypeExpression - case KindJSDocNullableType: - return n.AsJSDocNullableType().Type - case KindJSDocNonNullableType: - return n.AsJSDocNonNullableType().Type - case KindJSDocOptionalType: - return n.AsJSDocOptionalType().Type - case KindExportAssignment: - return n.AsExportAssignment().Type - case KindBinaryExpression: - return n.AsBinaryExpression().Type - default: - if funcLike := n.FunctionLikeData(); funcLike != nil { - return funcLike.Type + if offset := nodeTypeOffset[n.Kind]; offset != 0 { + t := *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) + if t != nil && t.Kind == KindUnknown { + return t.forceLazyTypeNode() } + return t + } + if funcLike := n.FunctionLikeData(); funcLike != nil { + return funcLike.Type } return nil } func (m *MutableNode) SetType(t *Node) { n := (*Node)(m) - switch m.Kind { - case KindVariableDeclaration: - n.AsVariableDeclaration().Type = t - case KindParameter: - n.AsParameterDeclaration().Type = t - case KindPropertySignature: - n.AsPropertySignatureDeclaration().Type = t - case KindPropertyDeclaration: - n.AsPropertyDeclaration().Type = t - case KindPropertyAssignment: - n.AsPropertyAssignment().Type = t - case KindShorthandPropertyAssignment: - n.AsShorthandPropertyAssignment().Type = t - case KindTypePredicate: - n.AsTypePredicateNode().Type = t - case KindParenthesizedType: - n.AsParenthesizedTypeNode().Type = t - case KindTypeOperator: - n.AsTypeOperatorNode().Type = t - case KindMappedType: - n.AsMappedTypeNode().Type = t - case KindTypeAssertionExpression: - n.AsTypeAssertion().Type = t - case KindAsExpression: - n.AsAsExpression().Type = t - case KindSatisfiesExpression: - n.AsSatisfiesExpression().Type = t - case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: - n.AsTypeAliasDeclaration().Type = t - case KindNamedTupleMember: - n.AsNamedTupleMember().Type = t - case KindOptionalType: - n.AsOptionalTypeNode().Type = t - case KindRestType: - n.AsRestTypeNode().Type = t - case KindTemplateLiteralTypeSpan: - n.AsTemplateLiteralTypeSpan().Type = t - case KindJSDocTypeExpression: - n.AsJSDocTypeExpression().Type = t - case KindJSDocParameterTag, KindJSDocPropertyTag: - n.AsJSDocParameterOrPropertyTag().TypeExpression = t - case KindJSDocNullableType: - n.AsJSDocNullableType().Type = t - case KindJSDocNonNullableType: - n.AsJSDocNonNullableType().Type = t - case KindJSDocOptionalType: - n.AsJSDocOptionalType().Type = t - case KindExportAssignment: - n.AsExportAssignment().Type = t - case KindBinaryExpression: - n.AsBinaryExpression().Type = t - default: - if funcLike := n.FunctionLikeData(); funcLike != nil { - funcLike.Type = t - } else { - panic("Unhandled case in mutableNode.SetType: " + n.Kind.String()) - } + if offset := nodeTypeOffset[n.Kind]; offset != 0 { + *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) = t + return } + if funcLike := n.FunctionLikeData(); funcLike != nil { + funcLike.Type = t + return + } + panic("Unhandled case in mutableNode.SetType") +} + +var nodeInitializerOffset = [kindFlowReduceLabelData + 1]uint8{ + KindVariableDeclaration: uint8(unsafe.Offsetof(VariableDeclaration{}.Initializer)), + KindParameter: uint8(unsafe.Offsetof(ParameterDeclaration{}.Initializer)), + KindBindingElement: uint8(unsafe.Offsetof(BindingElement{}.Initializer)), + KindPropertyDeclaration: uint8(unsafe.Offsetof(PropertyDeclaration{}.Initializer)), + KindPropertySignature: uint8(unsafe.Offsetof(PropertySignatureDeclaration{}.Initializer)), + KindPropertyAssignment: uint8(unsafe.Offsetof(PropertyAssignment{}.Initializer)), + KindEnumMember: uint8(unsafe.Offsetof(EnumMember{}.Initializer)), + KindForStatement: uint8(unsafe.Offsetof(ForStatement{}.Initializer)), + KindForInStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.Initializer)), + KindForOfStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.Initializer)), + KindJsxAttribute: uint8(unsafe.Offsetof(JsxAttribute{}.Initializer)), } func (n *Node) Initializer() *Node { - switch n.Kind { - case KindVariableDeclaration: - return n.AsVariableDeclaration().Initializer - case KindParameter: - return n.AsParameterDeclaration().Initializer - case KindBindingElement: - return n.AsBindingElement().Initializer - case KindPropertyDeclaration: - return n.AsPropertyDeclaration().Initializer - case KindPropertySignature: - return n.AsPropertySignatureDeclaration().Initializer - case KindPropertyAssignment: - return n.AsPropertyAssignment().Initializer - case KindEnumMember: - return n.AsEnumMember().Initializer - case KindForStatement: - return n.AsForStatement().Initializer - case KindForInStatement, KindForOfStatement: - return n.AsForInOrOfStatement().Initializer - case KindJsxAttribute: - return n.AsJsxAttribute().Initializer + if offset := nodeInitializerOffset[n.Kind]; offset != 0 { + return *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) } panic("Unhandled case in Node.Initializer") } func (m *MutableNode) SetInitializer(initializer *Node) { n := (*Node)(m) - switch n.Kind { - case KindVariableDeclaration: - n.AsVariableDeclaration().Initializer = initializer - case KindParameter: - n.AsParameterDeclaration().Initializer = initializer - case KindBindingElement: - n.AsBindingElement().Initializer = initializer - case KindPropertyDeclaration: - n.AsPropertyDeclaration().Initializer = initializer - case KindPropertySignature: - n.AsPropertySignatureDeclaration().Initializer = initializer - case KindPropertyAssignment: - n.AsPropertyAssignment().Initializer = initializer - case KindEnumMember: - n.AsEnumMember().Initializer = initializer - case KindForStatement: - n.AsForStatement().Initializer = initializer - case KindForInStatement, KindForOfStatement: - n.AsForInOrOfStatement().Initializer = initializer - case KindJsxAttribute: - n.AsJsxAttribute().Initializer = initializer - default: - panic("Unhandled case in mutableNode.SetInitializer") + if offset := nodeInitializerOffset[n.Kind]; offset != 0 { + *(**Node)(unsafe.Add(unsafe.Pointer(&n.Flags), offset-nodeFlagsOffset)) = initializer + return } + panic("Unhandled case in mutableNode.SetInitializer") } func (n *Node) TagName() *Node { @@ -1524,12 +1351,25 @@ func (node *Node) EagerJSDoc(file *SourceFile) []*Node { // CompositeBase +var subtreeFactsShards [64]struct { + mu sync.RWMutex + m map[*Node]SubtreeFacts +} + func (node *CompositeBase) subtreeFactsWorker(self *Node) SubtreeFacts { // computeSubtreeFacts() is expected to be idempotent, so races will only impact time, not correctness. - facts := SubtreeFacts(node.facts.Load()) + shard := &subtreeFactsShards[(uintptr(unsafe.Pointer(&self.Flags))>>4)&63] + shard.mu.RLock() + facts := shard.m[self] + shard.mu.RUnlock() if facts&SubtreeFactsComputed == 0 { - facts |= self.computeSubtreeFacts() | SubtreeFactsComputed - node.facts.Store(uint32(facts)) + facts = self.computeSubtreeFacts() | SubtreeFactsComputed + shard.mu.Lock() + if shard.m == nil { + shard.m = make(map[*Node]SubtreeFacts) + } + shard.m[self] = facts + shard.mu.Unlock() } return facts &^ SubtreeFactsComputed } @@ -2317,9 +2157,9 @@ type sourceFileDataKey uint64 var sourceFileDataKeyCounter atomic.Uint64 -type sourceFileDataCell[T any] struct { +type sourceFileDataCell struct { once sync.Once - value T + value unsafe.Pointer } func NewSourceFileDataKey[T any]() *SourceFileDataKey[T] { @@ -2327,15 +2167,16 @@ func NewSourceFileDataKey[T any]() *SourceFileDataKey[T] { } func (file *SourceFile) GetOrComputeData[T any](key *SourceFileDataKey[T], compute func(*SourceFile) T) T { - cell := file.getDataCell(key) + cell := file.getDataCell(key.key) cell.once.Do(func() { - cell.value = compute(file) + v := compute(file) + cell.value = unsafe.Pointer(&v) }) - return cell.value + return *(*T)(cell.value) } -func (file *SourceFile) getDataCell[T any](key *SourceFileDataKey[T]) *sourceFileDataCell[T] { - if key == nil || key.key == 0 { +func (file *SourceFile) getDataCell(key sourceFileDataKey) *sourceFileDataCell { + if key == 0 { panic("invalid SourceFileDataKey; use NewSourceFileDataKey") } @@ -2343,13 +2184,13 @@ func (file *SourceFile) getDataCell[T any](key *SourceFileDataKey[T]) *sourceFil defer file.dataMu.Unlock() if file.data == nil { - file.data = make(map[sourceFileDataKey]any) + file.data = make(map[sourceFileDataKey]*sourceFileDataCell) } - if cell, ok := file.data[key.key]; ok { - return cell.(*sourceFileDataCell[T]) + if cell, ok := file.data[key]; ok { + return cell } - cell := &sourceFileDataCell[T]{} - file.data[key.key] = cell + cell := &sourceFileDataCell{} + file.data[key] = cell return cell } @@ -2384,7 +2225,7 @@ type SourceFile struct { // Fields for lazily-computed data owned by packages outside ast. dataMu sync.Mutex - data map[sourceFileDataKey]any + data map[sourceFileDataKey]*sourceFileDataCell // Fields set by parser diagnostics []*Diagnostic @@ -2425,7 +2266,7 @@ type SourceFile struct { SymbolCount int PatternAmbientModules []*PatternAmbientModule GlobalExports SymbolTable - + LazyLists []*LazyNodeList // Fields set by ECMALineMap ecmaLineMapMu sync.RWMutex diff --git a/tsc/internal/ast/ast_generated.go b/tsc/internal/ast/ast_generated.go index 58aef6d7bc11c..f544b2f5a4904 100644 --- a/tsc/internal/ast/ast_generated.go +++ b/tsc/internal/ast/ast_generated.go @@ -20,11 +20,18 @@ var ( type NodeFactory struct { hooks NodeFactoryHooks + arrayLiteralExpressionArena core.Arena[ArrayLiteralExpression] arrayTypeNodeArena core.Arena[ArrayTypeNode] + arrowFunctionArena core.Arena[ArrowFunction] + asExpressionArena core.Arena[AsExpression] + awaitExpressionArena core.Arena[AwaitExpression] binaryExpressionArena core.Arena[BinaryExpression] + bindingElementArena core.Arena[BindingElement] + bindingPatternArena core.Arena[BindingPattern] blockArena core.Arena[Block] callExpressionArena core.Arena[CallExpression] conditionalExpressionArena core.Arena[ConditionalExpression] + conditionalTypeNodeArena core.Arena[ConditionalTypeNode] constructSignatureDeclarationArena core.Arena[ConstructSignatureDeclaration] elementAccessExpressionArena core.Arena[ElementAccessExpression] expressionStatementArena core.Arena[ExpressionStatement] @@ -34,8 +41,12 @@ type NodeFactory struct { heritageClauseArena core.Arena[HeritageClause] identifierArena core.Arena[Identifier] ifStatementArena core.Arena[IfStatement] + importClauseArena core.Arena[ImportClause] + importDeclarationArena core.Arena[ImportDeclaration] importSpecifierArena core.Arena[ImportSpecifier] + importTypeNodeArena core.Arena[ImportTypeNode] indexedAccessTypeNodeArena core.Arena[IndexedAccessTypeNode] + inferTypeNodeArena core.Arena[InferTypeNode] interfaceDeclarationArena core.Arena[InterfaceDeclaration] intersectionTypeNodeArena core.Arena[IntersectionTypeNode] jsdocArena core.Arena[JSDoc] @@ -45,24 +56,41 @@ type NodeFactory struct { keywordExpressionArena core.Arena[KeywordExpression] keywordTypeNodeArena core.Arena[KeywordTypeNode] literalTypeNodeArena core.Arena[LiteralTypeNode] + methodDeclarationArena core.Arena[MethodDeclaration] methodSignatureDeclarationArena core.Arena[MethodSignatureDeclaration] modifierListArena core.Arena[ModifierList] + namedImportsArena core.Arena[NamedImports] nodeListArena core.Arena[NodeList] numericLiteralArena core.Arena[NumericLiteral] + objectLiteralExpressionArena core.Arena[ObjectLiteralExpression] parameterDeclarationArena core.Arena[ParameterDeclaration] parenthesizedExpressionArena core.Arena[ParenthesizedExpression] parenthesizedTypeNodeArena core.Arena[ParenthesizedTypeNode] prefixUnaryExpressionArena core.Arena[PrefixUnaryExpression] propertyAccessExpressionArena core.Arena[PropertyAccessExpression] propertyAssignmentArena core.Arena[PropertyAssignment] + propertyDeclarationArena core.Arena[PropertyDeclaration] propertySignatureDeclarationArena core.Arena[PropertySignatureDeclaration] + restTypeNodeArena core.Arena[RestTypeNode] returnStatementArena core.Arena[ReturnStatement] + shorthandPropertyAssignmentArena core.Arena[ShorthandPropertyAssignment] stringLiteralArena core.Arena[StringLiteral] + templateExpressionArena core.Arena[TemplateExpression] + templateHeadArena core.Arena[TemplateHead] + templateLiteralTypeNodeArena core.Arena[TemplateLiteralTypeNode] + templateLiteralTypeSpanArena core.Arena[TemplateLiteralTypeSpan] + templateMiddleArena core.Arena[TemplateMiddle] + templateSpanArena core.Arena[TemplateSpan] + templateTailArena core.Arena[TemplateTail] + thisTypeNodeArena core.Arena[ThisTypeNode] tokenArena core.Arena[Token] + tupleTypeNodeArena core.Arena[TupleTypeNode] typeAliasDeclarationArena core.Arena[TypeAliasDeclaration] typeLiteralNodeArena core.Arena[TypeLiteralNode] + typeOfExpressionArena core.Arena[TypeOfExpression] typeOperatorNodeArena core.Arena[TypeOperatorNode] typeParameterDeclarationArena core.Arena[TypeParameterDeclaration] + typeQueryNodeArena core.Arena[TypeQueryNode] typeReferenceNodeArena core.Arena[TypeReferenceNode] unionTypeNodeArena core.Arena[UnionTypeNode] variableDeclarationArena core.Arena[VariableDeclaration] @@ -146,9 +174,7 @@ type FlowNodeBase struct { FlowNode *FlowNode } -type CompositeBase struct { - facts atomic.Uint32 -} +type CompositeBase struct{} type TypeSyntaxBase struct{} @@ -172,10 +198,10 @@ type FunctionLikeWithBodyBase struct { } type ClassLikeBase struct { + CompositeBase ExportableBase ModifiersBase LocalsContainerBase - CompositeBase name *IdentifierNode // Optional TypeParameters *TypeParameterList // Optional HeritageClauses *HeritageClauseList // Optional @@ -214,12 +240,12 @@ type AccessorDeclarationBase struct { TypeElementBase ClassElementBase ObjectLiteralElementBase + CompositeBase NodeBase DeclarationBase NamedMemberBase FunctionLikeWithBodyBase FlowNodeBase - CompositeBase } type FunctionOrConstructorTypeNodeBase struct { @@ -622,13 +648,13 @@ func (f *NodeFactory) NewToken(kind TokenSyntaxKind) *Node { data := &NoSubstitutionTemplateLiteral{} return f.newNode(kind, data.AsNode()) case KindTemplateHead: - data := &TemplateHead{} + data := f.templateHeadArena.New() return f.newNode(kind, data.AsNode()) case KindTemplateMiddle: - data := &TemplateMiddle{} + data := f.templateMiddleArena.New() return f.newNode(kind, data.AsNode()) case KindTemplateTail: - data := &TemplateTail{} + data := f.templateTailArena.New() return f.newNode(kind, data.AsNode()) case KindIdentifier: data := f.identifierArena.New() @@ -714,9 +740,9 @@ func IsPrivateIdentifier(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type QualifiedName struct { + CompositeBase NodeBase FlowNodeBase - CompositeBase Left *EntityName Right *MemberName } @@ -761,8 +787,8 @@ func IsQualifiedName(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ComputedPropertyName struct { - NodeBase CompositeBase + NodeBase Expression *Expression } @@ -804,8 +830,8 @@ func IsComputedPropertyName(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type Decorator struct { - NodeBase CompositeBase + NodeBase Expression *LeftHandSideExpression } @@ -864,8 +890,8 @@ func IsEmptyStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type IfStatement struct { - StatementBase CompositeBase + StatementBase Expression *Expression ThenStatement *Statement ElseStatement *Statement // Optional @@ -913,8 +939,8 @@ func IsIfStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type DoStatement struct { - IterationStatementBase CompositeBase + IterationStatementBase Expression *Expression } @@ -958,8 +984,8 @@ func IsDoStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type WhileStatement struct { - IterationStatementBase CompositeBase + IterationStatementBase Expression *Expression } @@ -1003,9 +1029,9 @@ func IsWhileStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ForStatement struct { + CompositeBase IterationStatementBase LocalsContainerBase - CompositeBase Initializer *ForInitializer // Optional Condition *Expression // Optional Incrementor *Expression // Optional @@ -1058,9 +1084,9 @@ func IsForStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ForInOrOfStatement struct { + CompositeBase StatementBase LocalsContainerBase - CompositeBase AwaitModifier *AwaitKeyword // Optional Initializer *ForInitializer Expression *Expression @@ -1192,8 +1218,8 @@ func IsContinueStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ReturnStatement struct { - StatementBase CompositeBase + StatementBase Expression *Expression // Optional } @@ -1231,8 +1257,8 @@ func IsReturnStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type WithStatement struct { - StatementBase CompositeBase + StatementBase Expression *Expression Statement *Statement } @@ -1277,8 +1303,8 @@ func IsWithStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type SwitchStatement struct { - StatementBase CompositeBase + StatementBase Expression *Expression CaseBlock *CaseBlockNode } @@ -1323,9 +1349,9 @@ func IsSwitchStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type CaseBlock struct { + CompositeBase NodeBase LocalsContainerBase - CompositeBase Clauses *CaseClausesList } @@ -1367,8 +1393,8 @@ func IsCaseBlock(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type CaseOrDefaultClause struct { - NodeBase CompositeBase + NodeBase Expression *Expression Statements *StatementList FallthroughFlowNode *FlowNode @@ -1423,8 +1449,8 @@ func IsDefaultClause(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ThrowStatement struct { - StatementBase CompositeBase + StatementBase Expression *Expression } @@ -1466,8 +1492,8 @@ func IsThrowStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type TryStatement struct { - StatementBase CompositeBase + StatementBase TryBlock *BlockNode CatchClause *CatchClauseNode // Optional FinallyBlock *BlockNode // Optional @@ -1515,9 +1541,9 @@ func IsTryStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type CatchClause struct { + CompositeBase NodeBase LocalsContainerBase - CompositeBase VariableDeclaration *VariableDeclarationNode // Optional Block *BlockNode } @@ -1665,9 +1691,9 @@ func IsExpressionStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type Block struct { + CompositeBase StatementBase LocalsContainerBase - CompositeBase Statements *StatementList MultiLine bool } @@ -1711,9 +1737,9 @@ func IsBlock(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type VariableStatement struct { + CompositeBase StatementBase ModifiersBase - CompositeBase DeclarationList *VariableDeclarationListNode } @@ -1752,10 +1778,10 @@ func IsVariableStatement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type VariableDeclaration struct { + CompositeBase NodeBase DeclarationBase ExportableBase - CompositeBase name *BindingName ExclamationToken *ExclamationToken // Optional Type *TypeNode // Optional @@ -1806,8 +1832,8 @@ func IsVariableDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type VariableDeclarationList struct { - NodeBase CompositeBase + NodeBase Declarations *VariableDeclarationNodeList } @@ -1847,8 +1873,8 @@ func IsVariableDeclarationList(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type BindingPattern struct { - NodeBase CompositeBase + NodeBase Elements *BindingElementList } @@ -1858,7 +1884,7 @@ func (f *NodeFactory) NewBindingPattern(kind Kind, elements *BindingElementList) default: panic("Invalid node kind for BindingPattern") } - data := &BindingPattern{} + data := f.bindingPatternArena.New() data.Elements = elements return f.newNode(kind, data.AsNode()) } @@ -1895,10 +1921,10 @@ func IsArrayBindingPattern(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ParameterDeclaration struct { + CompositeBase NodeBase DeclarationBase ModifiersBase - CompositeBase DotDotDotToken *DotDotDotToken // Optional name *BindingName QuestionToken *QuestionToken // Optional @@ -1954,11 +1980,11 @@ func IsParameterDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type BindingElement struct { + CompositeBase NodeBase DeclarationBase ExportableBase FlowNodeBase - CompositeBase DotDotDotToken *DotDotDotToken // Optional PropertyName *PropertyName // Optional name *BindingName // Optional @@ -1966,7 +1992,7 @@ type BindingElement struct { } func (f *NodeFactory) NewBindingElement(dotDotDotToken *DotDotDotToken, propertyName *PropertyName, name *BindingName, initializer *Expression) *Node { - data := &BindingElement{} + data := f.bindingElementArena.New() data.DotDotDotToken = dotDotDotToken data.PropertyName = propertyName data.name = name @@ -2048,12 +2074,12 @@ func IsMissingDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type FunctionDeclaration struct { + CompositeBase StatementBase DeclarationBase ExportableBase ModifiersBase FunctionLikeWithBodyBase - CompositeBase name *IdentifierNode // Optional ReturnFlowNode *FlowNode } @@ -2212,8 +2238,8 @@ func IsClassExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type HeritageClause struct { - NodeBase CompositeBase + NodeBase Token Kind Types *HeritageClauseElementList } @@ -2392,10 +2418,10 @@ func IsJSTypeAliasDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type EnumMember struct { + CompositeBase NodeBase DeclarationBase NamedMemberBase - CompositeBase Initializer *Expression // Optional } @@ -2438,11 +2464,11 @@ func IsEnumMember(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type EnumDeclaration struct { + CompositeBase StatementBase DeclarationBase ExportableBase ModifiersBase - CompositeBase name *IdentifierNode Members *EnumMemberList } @@ -2487,8 +2513,8 @@ func IsEnumDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ModuleBlock struct { - StatementBase CompositeBase + StatementBase Statements *StatementList } @@ -2574,9 +2600,9 @@ func IsNotEmittedTypeElement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportDeclaration struct { + CompositeBase StatementBase ModifiersBase - CompositeBase DeclarationBase ImportClause *ImportClauseNode // Optional ModuleSpecifier *Expression @@ -2584,7 +2610,7 @@ type ImportDeclaration struct { } func (f *NodeFactory) NewImportDeclaration(modifiers *ModifierList, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { - data := &ImportDeclaration{} + data := f.importDeclarationArena.New() data.modifiers = modifiers data.ImportClause = importClause data.ModuleSpecifier = moduleSpecifier @@ -2593,7 +2619,7 @@ func (f *NodeFactory) NewImportDeclaration(modifiers *ModifierList, importClause } func (f *NodeFactory) NewJSImportDeclaration(modifiers *ModifierList, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { - data := &ImportDeclaration{} + data := f.importDeclarationArena.New() data.modifiers = modifiers data.ImportClause = importClause data.ModuleSpecifier = moduleSpecifier @@ -2747,13 +2773,13 @@ func IsNamespaceImport(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type NamedImports struct { - NodeBase CompositeBase + NodeBase Elements *ImportSpecifierList } func (f *NodeFactory) NewNamedImports(elements *ImportSpecifierList) *Node { - data := &NamedImports{} + data := f.namedImportsArena.New() data.Elements = elements return f.newNode(KindNamedImports, data.AsNode()) } @@ -2790,10 +2816,10 @@ func IsNamedImports(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ExportAssignment struct { + CompositeBase StatementBase DeclarationBase ModifiersBase - CompositeBase IsExportEquals bool Type *TypeNode Expression *Expression @@ -2929,8 +2955,8 @@ func IsNamespaceExport(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type NamedExports struct { - NodeBase CompositeBase + NodeBase Elements *ExportSpecifierList } @@ -2972,10 +2998,10 @@ func IsNamedExports(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ExportSpecifier struct { + CompositeBase NodeBase DeclarationBase ExportableBase - CompositeBase IsTypeOnly bool PropertyName *ModuleExportName // Optional name *ModuleExportName @@ -3108,11 +3134,11 @@ func IsConstructSignatureDeclaration(node *Node) bool { type ConstructorDeclaration struct { ClassElementBase + CompositeBase NodeBase DeclarationBase ModifiersBase FunctionLikeWithBodyBase - CompositeBase ReturnFlowNode *FlowNode } @@ -3369,16 +3395,16 @@ func IsMethodSignatureDeclaration(node *Node) bool { type MethodDeclaration struct { ClassElementBase ObjectLiteralElementBase + CompositeBase NodeBase DeclarationBase NamedMemberBase FunctionLikeWithBodyBase FlowNodeBase - CompositeBase } func (f *NodeFactory) NewMethodDeclaration(modifiers *ModifierList, asteriskToken *AsteriskToken, name *PropertyName, postfixToken *TokenNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { - data := &MethodDeclaration{} + data := f.methodDeclarationArena.New() data.modifiers = modifiers data.AsteriskToken = asteriskToken data.name = name @@ -3487,16 +3513,16 @@ func IsPropertySignatureDeclaration(node *Node) bool { type PropertyDeclaration struct { ClassElementBase + CompositeBase NodeBase DeclarationBase NamedMemberBase - CompositeBase Type *TypeNode // Optional Initializer *Expression // Optional } func (f *NodeFactory) NewPropertyDeclaration(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { - data := &PropertyDeclaration{} + data := f.propertyDeclarationArena.New() data.modifiers = modifiers data.name = name data.PostfixToken = postfixToken @@ -3565,11 +3591,11 @@ func IsSemicolonClassElement(node *Node) bool { type ClassStaticBlockDeclaration struct { ClassElementBase + CompositeBase NodeBase DeclarationBase ModifiersBase LocalsContainerBase - CompositeBase Body *BlockNode ReturnFlowNode *FlowNode } @@ -3783,10 +3809,10 @@ func IsNoSubstitutionTemplateLiteral(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type BinaryExpression struct { + CompositeBase ExpressionBase DeclarationBase ModifiersBase - CompositeBase Left *Expression Type *TypeNode // Optional OperatorToken *BinaryOperatorToken @@ -3963,17 +3989,17 @@ func IsYieldExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ArrowFunction struct { + CompositeBase ExpressionBase DeclarationBase ModifiersBase FunctionLikeWithBodyBase FlowNodeBase - CompositeBase EqualsGreaterThanToken *EqualsGreaterThanToken } func (f *NodeFactory) NewArrowFunction(modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, equalsGreaterThanToken *EqualsGreaterThanToken, body *ConciseBody) *Node { - data := &ArrowFunction{} + data := f.arrowFunctionArena.New() data.modifiers = modifiers data.TypeParameters = typeParameters data.Parameters = parameters @@ -4018,12 +4044,12 @@ func IsArrowFunction(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type FunctionExpression struct { + CompositeBase PrimaryExpressionBase DeclarationBase ModifiersBase FunctionLikeWithBodyBase FlowNodeBase - CompositeBase name *IdentifierNode // Optional ReturnFlowNode *FlowNode } @@ -4086,7 +4112,7 @@ type AsExpression struct { } func (f *NodeFactory) NewAsExpression(expression *Expression, typeNode *TypeNode) *Node { - data := &AsExpression{} + data := f.asExpressionArena.New() data.Expression = expression data.Type = typeNode return f.newNode(KindAsExpression, data.AsNode()) @@ -4160,8 +4186,8 @@ func IsSatisfiesExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ConditionalExpression struct { - ExpressionBase CompositeBase + ExpressionBase Condition *Expression QuestionToken *QuestionToken WhenTrue *Expression @@ -4219,9 +4245,9 @@ func IsConditionalExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type PropertyAccessExpression struct { + CompositeBase MemberExpressionBase FlowNodeBase - CompositeBase Expression *Expression QuestionDotToken *QuestionDotToken // Optional name *MemberName @@ -4269,9 +4295,9 @@ func IsPropertyAccessExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ElementAccessExpression struct { + CompositeBase MemberExpressionBase FlowNodeBase - CompositeBase Expression *Expression QuestionDotToken *QuestionDotToken // Optional ArgumentExpression *Expression @@ -4321,9 +4347,9 @@ func IsElementAccessExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type CallExpression struct { + CompositeBase LeftHandSideExpressionBase DeclarationBase - CompositeBase Expression *Expression QuestionDotToken *QuestionDotToken // Optional TypeArguments *TypeList // Optional @@ -4372,8 +4398,8 @@ func IsCallExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type NewExpression struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase Expression *Expression TypeArguments *TypeList // Optional Arguments *ElementList // Optional @@ -4415,9 +4441,9 @@ func IsNewExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type MetaProperty struct { + CompositeBase PrimaryExpressionBase FlowNodeBase - CompositeBase KeywordToken Kind name *IdentifierNode } @@ -4539,14 +4565,14 @@ func IsSpreadElement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type TemplateExpression struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase Head *TemplateHeadNode TemplateSpans *TemplateSpanList } func (f *NodeFactory) NewTemplateExpression(head *TemplateHeadNode, templateSpans *TemplateSpanList) *Node { - data := &TemplateExpression{} + data := f.templateExpressionArena.New() data.Head = head data.TemplateSpans = templateSpans return f.newNode(KindTemplateExpression, data.AsNode()) @@ -4591,7 +4617,7 @@ type TemplateSpan struct { } func (f *NodeFactory) NewTemplateSpan(expression *Expression, literal *TemplateMiddleOrTail) *Node { - data := &TemplateSpan{} + data := f.templateSpanArena.New() data.Expression = expression data.Literal = literal return f.newNode(KindTemplateSpan, data.AsNode()) @@ -4630,8 +4656,8 @@ func IsTemplateSpan(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type TaggedTemplateExpression struct { - MemberExpressionBase CompositeBase + MemberExpressionBase Tag *Expression QuestionDotToken *QuestionDotToken TypeArguments *TypeList // Optional @@ -4722,14 +4748,14 @@ func IsParenthesizedExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ArrayLiteralExpression struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase Elements *ElementList MultiLine bool } func (f *NodeFactory) NewArrayLiteralExpression(elements *ElementList, multiLine bool) *Node { - data := &ArrayLiteralExpression{} + data := f.arrayLiteralExpressionArena.New() data.Elements = elements data.MultiLine = multiLine return f.newNode(KindArrayLiteralExpression, data.AsNode()) @@ -4767,15 +4793,15 @@ func IsArrayLiteralExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ObjectLiteralExpression struct { + CompositeBase PrimaryExpressionBase DeclarationBase - CompositeBase Properties *NodeList MultiLine bool } func (f *NodeFactory) NewObjectLiteralExpression(properties *NodeList, multiLine bool) *Node { - data := &ObjectLiteralExpression{} + data := f.objectLiteralExpressionArena.New() data.Properties = properties data.MultiLine = multiLine return f.newNode(KindObjectLiteralExpression, data.AsNode()) @@ -4854,10 +4880,10 @@ func IsSpreadAssignment(node *Node) bool { type PropertyAssignment struct { ObjectLiteralElementBase + CompositeBase NodeBase DeclarationBase NamedMemberBase - CompositeBase Type *TypeNode Initializer *Expression } @@ -4909,17 +4935,17 @@ func IsPropertyAssignment(node *Node) bool { type ShorthandPropertyAssignment struct { ObjectLiteralElementBase + CompositeBase NodeBase DeclarationBase NamedMemberBase - CompositeBase Type *TypeNode EqualsToken *EqualsToken // Optional ObjectAssignmentInitializer *Expression // Optional } func (f *NodeFactory) NewShorthandPropertyAssignment(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, equalsToken *EqualsToken, objectAssignmentInitializer *Expression) *Node { - data := &ShorthandPropertyAssignment{} + data := f.shorthandPropertyAssignmentArena.New() data.modifiers = modifiers data.name = name data.PostfixToken = postfixToken @@ -5013,7 +5039,7 @@ type TypeOfExpression struct { } func (f *NodeFactory) NewTypeOfExpression(expression *Expression) *Node { - data := &TypeOfExpression{} + data := f.typeOfExpressionArena.New() data.Expression = expression return f.newNode(KindTypeOfExpression, data.AsNode()) } @@ -5097,7 +5123,7 @@ type AwaitExpression struct { } func (f *NodeFactory) NewAwaitExpression(expression *Expression) *Node { - data := &AwaitExpression{} + data := f.awaitExpressionArena.New() data.Expression = expression return f.newNode(KindAwaitExpression, data.AsNode()) } @@ -5283,7 +5309,7 @@ type ConditionalTypeNode struct { } func (f *NodeFactory) NewConditionalTypeNode(checkType *TypeNode, extendsType *TypeNode, trueType *TypeNode, falseType *TypeNode) *Node { - data := &ConditionalTypeNode{} + data := f.conditionalTypeNodeArena.New() data.CheckType = checkType data.ExtendsType = extendsType data.TrueType = trueType @@ -5367,7 +5393,7 @@ type InferTypeNode struct { } func (f *NodeFactory) NewInferTypeNode(typeParameter *TypeParameterDeclarationNode) *Node { - data := &InferTypeNode{} + data := f.inferTypeNodeArena.New() data.TypeParameter = typeParameter return f.newNode(KindInferType, data.AsNode()) } @@ -5517,8 +5543,8 @@ func IsTypeReferenceNode(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ExpressionWithTypeArguments struct { - MemberExpressionBase CompositeBase + MemberExpressionBase Expression *Expression TypeArguments *TypeList // Optional } @@ -5600,7 +5626,7 @@ type ThisTypeNode struct { } func (f *NodeFactory) NewThisTypeNode() *Node { - data := &ThisTypeNode{} + data := f.thisTypeNodeArena.New() return f.newNode(KindThisType, data.AsNode()) } @@ -5659,8 +5685,8 @@ func IsTypePredicateNode(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportAttribute struct { - NodeBase CompositeBase + NodeBase name *ImportAttributeName Value *Expression } @@ -5709,8 +5735,8 @@ func IsImportAttribute(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportAttributes struct { - NodeBase CompositeBase + NodeBase Token Kind Attributes *ImportAttributeList MultiLine bool @@ -5761,7 +5787,7 @@ type TypeQueryNode struct { } func (f *NodeFactory) NewTypeQueryNode(exprName *EntityName, typeArguments *TypeList) *Node { - data := &TypeQueryNode{} + data := f.typeQueryNodeArena.New() data.ExprName = exprName data.TypeArguments = typeArguments return f.newNode(KindTypeQuery, data.AsNode()) @@ -5894,7 +5920,7 @@ type TupleTypeNode struct { } func (f *NodeFactory) NewTupleTypeNode(elements *TypeList) *Node { - data := &TupleTypeNode{} + data := f.tupleTypeNodeArena.New() data.Elements = elements return f.newNode(KindTupleType, data.AsNode()) } @@ -6022,7 +6048,7 @@ type RestTypeNode struct { } func (f *NodeFactory) NewRestTypeNode(typeNode *TypeNode) *Node { - data := &RestTypeNode{} + data := f.restTypeNodeArena.New() data.Type = typeNode return f.newNode(KindRestType, data.AsNode()) } @@ -6180,7 +6206,7 @@ type TemplateHead struct { } func (f *NodeFactory) NewTemplateHead(text string, rawText string, templateFlags TokenFlags) *Node { - data := &TemplateHead{} + data := f.templateHeadArena.New() data.Text = text data.RawText = rawText data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags @@ -6206,7 +6232,7 @@ type TemplateMiddle struct { } func (f *NodeFactory) NewTemplateMiddle(text string, rawText string, templateFlags TokenFlags) *Node { - data := &TemplateMiddle{} + data := f.templateMiddleArena.New() data.Text = text data.RawText = rawText data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags @@ -6232,7 +6258,7 @@ type TemplateTail struct { } func (f *NodeFactory) NewTemplateTail(text string, rawText string, templateFlags TokenFlags) *Node { - data := &TemplateTail{} + data := f.templateTailArena.New() data.Text = text data.RawText = rawText data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags @@ -6259,7 +6285,7 @@ type TemplateLiteralTypeNode struct { } func (f *NodeFactory) NewTemplateLiteralTypeNode(head *TemplateHeadNode, templateSpans *TemplateLiteralTypeSpanList) *Node { - data := &TemplateLiteralTypeNode{} + data := f.templateLiteralTypeNodeArena.New() data.Head = head data.TemplateSpans = templateSpans return f.newNode(KindTemplateLiteralType, data.AsNode()) @@ -6299,7 +6325,7 @@ type TemplateLiteralTypeSpan struct { } func (f *NodeFactory) NewTemplateLiteralTypeSpan(typeNode *TypeNode, literal *TemplateMiddleOrTail) *Node { - data := &TemplateLiteralTypeSpan{} + data := f.templateLiteralTypeSpanArena.New() data.Type = typeNode data.Literal = literal return f.newNode(KindTemplateLiteralTypeSpan, data.AsNode()) @@ -6417,8 +6443,8 @@ func IsPartiallyEmittedExpression(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxElement struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase OpeningElement *JsxOpeningElementNode Children *JsxChildList ClosingElement *JsxClosingElementNode @@ -6460,9 +6486,9 @@ func IsJsxElement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxAttributes struct { + CompositeBase PrimaryExpressionBase DeclarationBase - CompositeBase Properties *JsxAttributeList } @@ -6500,8 +6526,8 @@ func IsJsxAttributes(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxNamespacedName struct { - ExpressionBase CompositeBase + ExpressionBase Namespace *IdentifierNode name *IdentifierNode } @@ -6545,8 +6571,8 @@ func IsJsxNamespacedName(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxOpeningElement struct { - ExpressionBase CompositeBase + ExpressionBase TagName *JsxTagNameExpression TypeArguments *TypeList // Optional Attributes *JsxAttributesNode @@ -6588,8 +6614,8 @@ func IsJsxOpeningElement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxSelfClosingElement struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase TagName *JsxTagNameExpression TypeArguments *TypeList // Optional Attributes *JsxAttributesNode @@ -6631,8 +6657,8 @@ func IsJsxSelfClosingElement(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxFragment struct { - PrimaryExpressionBase CompositeBase + PrimaryExpressionBase OpeningFragment *JsxOpeningFragmentNode Children *JsxChildList ClosingFragment *JsxClosingFragmentNode @@ -6716,9 +6742,9 @@ func IsJsxClosingFragment(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type JsxAttribute struct { + CompositeBase NodeBase DeclarationBase - CompositeBase name *JsxAttributeName Initializer *JsxAttributeValue // Optional } @@ -8105,13 +8131,13 @@ func IsSourceFile(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ModuleDeclaration struct { + CompositeBase StatementBase DeclarationBase ExportableBase ModifiersBase LocalsContainerBase BodyBase - CompositeBase Keyword Kind name *ModuleName Attributes *TypeLiteralNodeNode // Optional @@ -8162,11 +8188,11 @@ func IsModuleDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportEqualsDeclaration struct { + CompositeBase StatementBase DeclarationBase ExportableBase ModifiersBase - CompositeBase IsTypeOnly bool name *IdentifierNode ModuleReference *ModuleReference @@ -8213,10 +8239,10 @@ func IsImportEqualsDeclaration(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ExportDeclaration struct { + CompositeBase StatementBase DeclarationBase ModifiersBase - CompositeBase IsTypeOnly bool ExportClause *NamedExportBindings // Optional ModuleSpecifier *Expression // Optional @@ -8272,7 +8298,7 @@ type ImportTypeNode struct { } func (f *NodeFactory) NewImportTypeNode(isTypeOf bool, argument *TypeNode, attributes *ImportAttributesNode, qualifier *EntityName, typeArguments *TypeList) *Node { - data := &ImportTypeNode{} + data := f.importTypeNodeArena.New() data.IsTypeOf = isTypeOf data.Argument = argument data.Attributes = attributes @@ -8312,17 +8338,17 @@ func IsImportTypeNode(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportClause struct { + CompositeBase NodeBase DeclarationBase ExportableBase - CompositeBase PhaseModifier ImportPhaseModifierSyntaxKind // Optional name *IdentifierNode // Optional NamedBindings *NamedImportBindings // Optional } func (f *NodeFactory) NewImportClause(phaseModifier ImportPhaseModifierSyntaxKind, name *IdentifierNode, namedBindings *NamedImportBindings) *Node { - data := &ImportClause{} + data := f.importClauseArena.New() data.PhaseModifier = phaseModifier data.name = name data.NamedBindings = namedBindings @@ -8361,10 +8387,10 @@ func IsImportClause(node *Node) bool { // ────────────────────────────────────────────────────────────────────── type ImportSpecifier struct { + CompositeBase NodeBase DeclarationBase ExportableBase - CompositeBase IsTypeOnly bool PropertyName *ModuleExportName // Optional name *IdentifierNode @@ -11033,628 +11059,284 @@ func (n *Node) propagateSubtreeFacts() SubtreeFacts { // ────────────────────────────────────────────────────────────────────── var nodeFlowNodeDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindIdentifier: 1, - KindQualifiedName: 2, - KindEmptyStatement: 3, - KindIfStatement: 4, - KindDoStatement: 5, - KindWhileStatement: 6, - KindForStatement: 7, - KindForInStatement: 8, - KindForOfStatement: 8, - KindBreakStatement: 9, - KindContinueStatement: 10, - KindReturnStatement: 11, - KindWithStatement: 12, - KindSwitchStatement: 13, - KindThrowStatement: 14, - KindTryStatement: 15, - KindDebuggerStatement: 16, - KindLabeledStatement: 17, - KindExpressionStatement: 18, - KindBlock: 19, - KindVariableStatement: 20, - KindBindingElement: 21, - KindMissingDeclaration: 22, - KindFunctionDeclaration: 23, - KindClassDeclaration: 24, - KindInterfaceDeclaration: 25, - KindTypeAliasDeclaration: 26, - KindJSTypeAliasDeclaration: 26, - KindEnumDeclaration: 27, - KindModuleBlock: 28, - KindNotEmittedStatement: 29, - KindImportDeclaration: 30, - KindJSImportDeclaration: 30, - KindExportAssignment: 31, - KindNamespaceExportDeclaration: 32, - KindGetAccessor: 33, - KindSetAccessor: 34, - KindMethodDeclaration: 35, - KindNullKeyword: 36, - KindTrueKeyword: 36, - KindFalseKeyword: 36, - KindThisKeyword: 36, - KindSuperKeyword: 36, - KindImportKeyword: 36, - KindArrowFunction: 37, - KindFunctionExpression: 38, - KindPropertyAccessExpression: 39, - KindElementAccessExpression: 40, - KindMetaProperty: 41, - KindModuleDeclaration: 42, - KindImportEqualsDeclaration: 43, - KindExportDeclaration: 44, + KindIdentifier: uint8(unsafe.Offsetof(Identifier{}.FlowNodeBase)), + KindQualifiedName: uint8(unsafe.Offsetof(QualifiedName{}.FlowNodeBase)), + KindEmptyStatement: uint8(unsafe.Offsetof(EmptyStatement{}.FlowNodeBase)), + KindIfStatement: uint8(unsafe.Offsetof(IfStatement{}.FlowNodeBase)), + KindDoStatement: uint8(unsafe.Offsetof(DoStatement{}.FlowNodeBase)), + KindWhileStatement: uint8(unsafe.Offsetof(WhileStatement{}.FlowNodeBase)), + KindForStatement: uint8(unsafe.Offsetof(ForStatement{}.FlowNodeBase)), + KindForInStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.FlowNodeBase)), + KindForOfStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.FlowNodeBase)), + KindBreakStatement: uint8(unsafe.Offsetof(BreakStatement{}.FlowNodeBase)), + KindContinueStatement: uint8(unsafe.Offsetof(ContinueStatement{}.FlowNodeBase)), + KindReturnStatement: uint8(unsafe.Offsetof(ReturnStatement{}.FlowNodeBase)), + KindWithStatement: uint8(unsafe.Offsetof(WithStatement{}.FlowNodeBase)), + KindSwitchStatement: uint8(unsafe.Offsetof(SwitchStatement{}.FlowNodeBase)), + KindThrowStatement: uint8(unsafe.Offsetof(ThrowStatement{}.FlowNodeBase)), + KindTryStatement: uint8(unsafe.Offsetof(TryStatement{}.FlowNodeBase)), + KindDebuggerStatement: uint8(unsafe.Offsetof(DebuggerStatement{}.FlowNodeBase)), + KindLabeledStatement: uint8(unsafe.Offsetof(LabeledStatement{}.FlowNodeBase)), + KindExpressionStatement: uint8(unsafe.Offsetof(ExpressionStatement{}.FlowNodeBase)), + KindBlock: uint8(unsafe.Offsetof(Block{}.FlowNodeBase)), + KindVariableStatement: uint8(unsafe.Offsetof(VariableStatement{}.FlowNodeBase)), + KindBindingElement: uint8(unsafe.Offsetof(BindingElement{}.FlowNodeBase)), + KindMissingDeclaration: uint8(unsafe.Offsetof(MissingDeclaration{}.FlowNodeBase)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.FlowNodeBase)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.FlowNodeBase)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.FlowNodeBase)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.FlowNodeBase)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.FlowNodeBase)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.FlowNodeBase)), + KindModuleBlock: uint8(unsafe.Offsetof(ModuleBlock{}.FlowNodeBase)), + KindNotEmittedStatement: uint8(unsafe.Offsetof(NotEmittedStatement{}.FlowNodeBase)), + KindImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.FlowNodeBase)), + KindJSImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.FlowNodeBase)), + KindExportAssignment: uint8(unsafe.Offsetof(ExportAssignment{}.FlowNodeBase)), + KindNamespaceExportDeclaration: uint8(unsafe.Offsetof(NamespaceExportDeclaration{}.FlowNodeBase)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.FlowNodeBase)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.FlowNodeBase)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.FlowNodeBase)), + KindNullKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindTrueKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindFalseKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindThisKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindSuperKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindImportKeyword: uint8(unsafe.Offsetof(KeywordExpression{}.FlowNodeBase)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.FlowNodeBase)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.FlowNodeBase)), + KindPropertyAccessExpression: uint8(unsafe.Offsetof(PropertyAccessExpression{}.FlowNodeBase)), + KindElementAccessExpression: uint8(unsafe.Offsetof(ElementAccessExpression{}.FlowNodeBase)), + KindMetaProperty: uint8(unsafe.Offsetof(MetaProperty{}.FlowNodeBase)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.FlowNodeBase)), + KindImportEqualsDeclaration: uint8(unsafe.Offsetof(ImportEqualsDeclaration{}.FlowNodeBase)), + KindExportDeclaration: uint8(unsafe.Offsetof(ExportDeclaration{}.FlowNodeBase)), } func (n *Node) FlowNodeData() *FlowNodeBase { - switch nodeFlowNodeDataDispatch[n.Kind] { - case 1: - return (*Identifier)(unsafe.Pointer(n)).FlowNodeData() - case 2: - return (*QualifiedName)(unsafe.Pointer(n)).FlowNodeData() - case 3: - return (*EmptyStatement)(unsafe.Pointer(n)).FlowNodeData() - case 4: - return (*IfStatement)(unsafe.Pointer(n)).FlowNodeData() - case 5: - return (*DoStatement)(unsafe.Pointer(n)).FlowNodeData() - case 6: - return (*WhileStatement)(unsafe.Pointer(n)).FlowNodeData() - case 7: - return (*ForStatement)(unsafe.Pointer(n)).FlowNodeData() - case 8: - return (*ForInOrOfStatement)(unsafe.Pointer(n)).FlowNodeData() - case 9: - return (*BreakStatement)(unsafe.Pointer(n)).FlowNodeData() - case 10: - return (*ContinueStatement)(unsafe.Pointer(n)).FlowNodeData() - case 11: - return (*ReturnStatement)(unsafe.Pointer(n)).FlowNodeData() - case 12: - return (*WithStatement)(unsafe.Pointer(n)).FlowNodeData() - case 13: - return (*SwitchStatement)(unsafe.Pointer(n)).FlowNodeData() - case 14: - return (*ThrowStatement)(unsafe.Pointer(n)).FlowNodeData() - case 15: - return (*TryStatement)(unsafe.Pointer(n)).FlowNodeData() - case 16: - return (*DebuggerStatement)(unsafe.Pointer(n)).FlowNodeData() - case 17: - return (*LabeledStatement)(unsafe.Pointer(n)).FlowNodeData() - case 18: - return (*ExpressionStatement)(unsafe.Pointer(n)).FlowNodeData() - case 19: - return (*Block)(unsafe.Pointer(n)).FlowNodeData() - case 20: - return (*VariableStatement)(unsafe.Pointer(n)).FlowNodeData() - case 21: - return (*BindingElement)(unsafe.Pointer(n)).FlowNodeData() - case 22: - return (*MissingDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 23: - return (*FunctionDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 24: - return (*ClassDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 25: - return (*InterfaceDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 26: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 27: - return (*EnumDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 28: - return (*ModuleBlock)(unsafe.Pointer(n)).FlowNodeData() - case 29: - return (*NotEmittedStatement)(unsafe.Pointer(n)).FlowNodeData() - case 30: - return (*ImportDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 31: - return (*ExportAssignment)(unsafe.Pointer(n)).FlowNodeData() - case 32: - return (*NamespaceExportDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 33: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 34: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 35: - return (*MethodDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 36: - return (*KeywordExpression)(unsafe.Pointer(n)).FlowNodeData() - case 37: - return (*ArrowFunction)(unsafe.Pointer(n)).FlowNodeData() - case 38: - return (*FunctionExpression)(unsafe.Pointer(n)).FlowNodeData() - case 39: - return (*PropertyAccessExpression)(unsafe.Pointer(n)).FlowNodeData() - case 40: - return (*ElementAccessExpression)(unsafe.Pointer(n)).FlowNodeData() - case 41: - return (*MetaProperty)(unsafe.Pointer(n)).FlowNodeData() - case 42: - return (*ModuleDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 43: - return (*ImportEqualsDeclaration)(unsafe.Pointer(n)).FlowNodeData() - case 44: - return (*ExportDeclaration)(unsafe.Pointer(n)).FlowNodeData() - default: - return nil + if offset := nodeFlowNodeDataDispatch[n.Kind]; offset != 0 { + return (*FlowNodeBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeDeclarationDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindVariableDeclaration: 1, - KindParameter: 2, - KindBindingElement: 3, - KindMissingDeclaration: 4, - KindFunctionDeclaration: 5, - KindClassDeclaration: 6, - KindClassExpression: 7, - KindInterfaceDeclaration: 8, - KindTypeAliasDeclaration: 9, - KindJSTypeAliasDeclaration: 9, - KindEnumMember: 10, - KindEnumDeclaration: 11, - KindNotEmittedTypeElement: 12, - KindImportDeclaration: 13, - KindJSImportDeclaration: 13, - KindNamespaceImport: 14, - KindExportAssignment: 15, - KindNamespaceExportDeclaration: 16, - KindNamespaceExport: 17, - KindExportSpecifier: 18, - KindCallSignature: 19, - KindConstructSignature: 20, - KindConstructor: 21, - KindGetAccessor: 22, - KindSetAccessor: 23, - KindIndexSignature: 24, - KindMethodSignature: 25, - KindMethodDeclaration: 26, - KindPropertySignature: 27, - KindPropertyDeclaration: 28, - KindSemicolonClassElement: 29, - KindClassStaticBlockDeclaration: 30, - KindNoSubstitutionTemplateLiteral: 31, - KindBinaryExpression: 32, - KindArrowFunction: 33, - KindFunctionExpression: 34, - KindCallExpression: 35, - KindObjectLiteralExpression: 36, - KindSpreadAssignment: 37, - KindPropertyAssignment: 38, - KindShorthandPropertyAssignment: 39, - KindMappedType: 40, - KindTypeLiteral: 41, - KindNamedTupleMember: 42, - KindFunctionType: 43, - KindConstructorType: 44, - KindJsxAttributes: 45, - KindJsxAttribute: 46, - KindJsxSpreadAttribute: 47, - KindJSDocSignature: 48, - KindSourceFile: 49, - KindModuleDeclaration: 50, - KindImportEqualsDeclaration: 51, - KindExportDeclaration: 52, - KindImportClause: 53, - KindImportSpecifier: 54, - KindTypeParameter: 55, - KindJSDocTypeLiteral: 56, + KindVariableDeclaration: uint8(unsafe.Offsetof(VariableDeclaration{}.DeclarationBase)), + KindParameter: uint8(unsafe.Offsetof(ParameterDeclaration{}.DeclarationBase)), + KindBindingElement: uint8(unsafe.Offsetof(BindingElement{}.DeclarationBase)), + KindMissingDeclaration: uint8(unsafe.Offsetof(MissingDeclaration{}.DeclarationBase)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.DeclarationBase)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.DeclarationBase)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.DeclarationBase)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.DeclarationBase)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.DeclarationBase)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.DeclarationBase)), + KindEnumMember: uint8(unsafe.Offsetof(EnumMember{}.DeclarationBase)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.DeclarationBase)), + KindNotEmittedTypeElement: uint8(unsafe.Offsetof(NotEmittedTypeElement{}.DeclarationBase)), + KindImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.DeclarationBase)), + KindJSImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.DeclarationBase)), + KindNamespaceImport: uint8(unsafe.Offsetof(NamespaceImport{}.DeclarationBase)), + KindExportAssignment: uint8(unsafe.Offsetof(ExportAssignment{}.DeclarationBase)), + KindNamespaceExportDeclaration: uint8(unsafe.Offsetof(NamespaceExportDeclaration{}.DeclarationBase)), + KindNamespaceExport: uint8(unsafe.Offsetof(NamespaceExport{}.DeclarationBase)), + KindExportSpecifier: uint8(unsafe.Offsetof(ExportSpecifier{}.DeclarationBase)), + KindCallSignature: uint8(unsafe.Offsetof(CallSignatureDeclaration{}.DeclarationBase)), + KindConstructSignature: uint8(unsafe.Offsetof(ConstructSignatureDeclaration{}.DeclarationBase)), + KindConstructor: uint8(unsafe.Offsetof(ConstructorDeclaration{}.DeclarationBase)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.DeclarationBase)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.DeclarationBase)), + KindIndexSignature: uint8(unsafe.Offsetof(IndexSignatureDeclaration{}.DeclarationBase)), + KindMethodSignature: uint8(unsafe.Offsetof(MethodSignatureDeclaration{}.DeclarationBase)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.DeclarationBase)), + KindPropertySignature: uint8(unsafe.Offsetof(PropertySignatureDeclaration{}.DeclarationBase)), + KindPropertyDeclaration: uint8(unsafe.Offsetof(PropertyDeclaration{}.DeclarationBase)), + KindSemicolonClassElement: uint8(unsafe.Offsetof(SemicolonClassElement{}.DeclarationBase)), + KindClassStaticBlockDeclaration: uint8(unsafe.Offsetof(ClassStaticBlockDeclaration{}.DeclarationBase)), + KindNoSubstitutionTemplateLiteral: uint8(unsafe.Offsetof(NoSubstitutionTemplateLiteral{}.DeclarationBase)), + KindBinaryExpression: uint8(unsafe.Offsetof(BinaryExpression{}.DeclarationBase)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.DeclarationBase)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.DeclarationBase)), + KindCallExpression: uint8(unsafe.Offsetof(CallExpression{}.DeclarationBase)), + KindObjectLiteralExpression: uint8(unsafe.Offsetof(ObjectLiteralExpression{}.DeclarationBase)), + KindSpreadAssignment: uint8(unsafe.Offsetof(SpreadAssignment{}.DeclarationBase)), + KindPropertyAssignment: uint8(unsafe.Offsetof(PropertyAssignment{}.DeclarationBase)), + KindShorthandPropertyAssignment: uint8(unsafe.Offsetof(ShorthandPropertyAssignment{}.DeclarationBase)), + KindMappedType: uint8(unsafe.Offsetof(MappedTypeNode{}.DeclarationBase)), + KindTypeLiteral: uint8(unsafe.Offsetof(TypeLiteralNode{}.DeclarationBase)), + KindNamedTupleMember: uint8(unsafe.Offsetof(NamedTupleMember{}.DeclarationBase)), + KindFunctionType: uint8(unsafe.Offsetof(FunctionTypeNode{}.DeclarationBase)), + KindConstructorType: uint8(unsafe.Offsetof(ConstructorTypeNode{}.DeclarationBase)), + KindJsxAttributes: uint8(unsafe.Offsetof(JsxAttributes{}.DeclarationBase)), + KindJsxAttribute: uint8(unsafe.Offsetof(JsxAttribute{}.DeclarationBase)), + KindJsxSpreadAttribute: uint8(unsafe.Offsetof(JsxSpreadAttribute{}.DeclarationBase)), + KindJSDocSignature: uint8(unsafe.Offsetof(JSDocSignature{}.DeclarationBase)), + KindSourceFile: uint8(unsafe.Offsetof(SourceFile{}.DeclarationBase)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.DeclarationBase)), + KindImportEqualsDeclaration: uint8(unsafe.Offsetof(ImportEqualsDeclaration{}.DeclarationBase)), + KindExportDeclaration: uint8(unsafe.Offsetof(ExportDeclaration{}.DeclarationBase)), + KindImportClause: uint8(unsafe.Offsetof(ImportClause{}.DeclarationBase)), + KindImportSpecifier: uint8(unsafe.Offsetof(ImportSpecifier{}.DeclarationBase)), + KindTypeParameter: uint8(unsafe.Offsetof(TypeParameterDeclaration{}.DeclarationBase)), + KindJSDocTypeLiteral: uint8(unsafe.Offsetof(JSDocTypeLiteral{}.DeclarationBase)), } func (n *Node) DeclarationData() *DeclarationBase { - switch nodeDeclarationDataDispatch[n.Kind] { - case 1: - return (*VariableDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 2: - return (*ParameterDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 3: - return (*BindingElement)(unsafe.Pointer(n)).DeclarationData() - case 4: - return (*MissingDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 5: - return (*FunctionDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 6: - return (*ClassDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 7: - return (*ClassExpression)(unsafe.Pointer(n)).DeclarationData() - case 8: - return (*InterfaceDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 9: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 10: - return (*EnumMember)(unsafe.Pointer(n)).DeclarationData() - case 11: - return (*EnumDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 12: - return (*NotEmittedTypeElement)(unsafe.Pointer(n)).DeclarationData() - case 13: - return (*ImportDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 14: - return (*NamespaceImport)(unsafe.Pointer(n)).DeclarationData() - case 15: - return (*ExportAssignment)(unsafe.Pointer(n)).DeclarationData() - case 16: - return (*NamespaceExportDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 17: - return (*NamespaceExport)(unsafe.Pointer(n)).DeclarationData() - case 18: - return (*ExportSpecifier)(unsafe.Pointer(n)).DeclarationData() - case 19: - return (*CallSignatureDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 20: - return (*ConstructSignatureDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 21: - return (*ConstructorDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 22: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 23: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 24: - return (*IndexSignatureDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 25: - return (*MethodSignatureDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 26: - return (*MethodDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 27: - return (*PropertySignatureDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 28: - return (*PropertyDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 29: - return (*SemicolonClassElement)(unsafe.Pointer(n)).DeclarationData() - case 30: - return (*ClassStaticBlockDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 31: - return (*NoSubstitutionTemplateLiteral)(unsafe.Pointer(n)).DeclarationData() - case 32: - return (*BinaryExpression)(unsafe.Pointer(n)).DeclarationData() - case 33: - return (*ArrowFunction)(unsafe.Pointer(n)).DeclarationData() - case 34: - return (*FunctionExpression)(unsafe.Pointer(n)).DeclarationData() - case 35: - return (*CallExpression)(unsafe.Pointer(n)).DeclarationData() - case 36: - return (*ObjectLiteralExpression)(unsafe.Pointer(n)).DeclarationData() - case 37: - return (*SpreadAssignment)(unsafe.Pointer(n)).DeclarationData() - case 38: - return (*PropertyAssignment)(unsafe.Pointer(n)).DeclarationData() - case 39: - return (*ShorthandPropertyAssignment)(unsafe.Pointer(n)).DeclarationData() - case 40: - return (*MappedTypeNode)(unsafe.Pointer(n)).DeclarationData() - case 41: - return (*TypeLiteralNode)(unsafe.Pointer(n)).DeclarationData() - case 42: - return (*NamedTupleMember)(unsafe.Pointer(n)).DeclarationData() - case 43: - return (*FunctionTypeNode)(unsafe.Pointer(n)).DeclarationData() - case 44: - return (*ConstructorTypeNode)(unsafe.Pointer(n)).DeclarationData() - case 45: - return (*JsxAttributes)(unsafe.Pointer(n)).DeclarationData() - case 46: - return (*JsxAttribute)(unsafe.Pointer(n)).DeclarationData() - case 47: - return (*JsxSpreadAttribute)(unsafe.Pointer(n)).DeclarationData() - case 48: - return (*JSDocSignature)(unsafe.Pointer(n)).DeclarationData() - case 49: - return (*SourceFile)(unsafe.Pointer(n)).DeclarationData() - case 50: - return (*ModuleDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 51: - return (*ImportEqualsDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 52: - return (*ExportDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 53: - return (*ImportClause)(unsafe.Pointer(n)).DeclarationData() - case 54: - return (*ImportSpecifier)(unsafe.Pointer(n)).DeclarationData() - case 55: - return (*TypeParameterDeclaration)(unsafe.Pointer(n)).DeclarationData() - case 56: - return (*JSDocTypeLiteral)(unsafe.Pointer(n)).DeclarationData() - default: - return nil + if offset := nodeDeclarationDataDispatch[n.Kind]; offset != 0 { + return (*DeclarationBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeExportableDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindVariableDeclaration: 1, - KindBindingElement: 2, - KindFunctionDeclaration: 3, - KindClassDeclaration: 4, - KindClassExpression: 5, - KindInterfaceDeclaration: 6, - KindTypeAliasDeclaration: 7, - KindJSTypeAliasDeclaration: 7, - KindEnumDeclaration: 8, - KindNamespaceImport: 9, - KindExportSpecifier: 10, - KindModuleDeclaration: 11, - KindImportEqualsDeclaration: 12, - KindImportClause: 13, - KindImportSpecifier: 14, + KindVariableDeclaration: uint8(unsafe.Offsetof(VariableDeclaration{}.ExportableBase)), + KindBindingElement: uint8(unsafe.Offsetof(BindingElement{}.ExportableBase)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.ExportableBase)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.ExportableBase)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.ExportableBase)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.ExportableBase)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.ExportableBase)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.ExportableBase)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.ExportableBase)), + KindNamespaceImport: uint8(unsafe.Offsetof(NamespaceImport{}.ExportableBase)), + KindExportSpecifier: uint8(unsafe.Offsetof(ExportSpecifier{}.ExportableBase)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.ExportableBase)), + KindImportEqualsDeclaration: uint8(unsafe.Offsetof(ImportEqualsDeclaration{}.ExportableBase)), + KindImportClause: uint8(unsafe.Offsetof(ImportClause{}.ExportableBase)), + KindImportSpecifier: uint8(unsafe.Offsetof(ImportSpecifier{}.ExportableBase)), } func (n *Node) ExportableData() *ExportableBase { - switch nodeExportableDataDispatch[n.Kind] { - case 1: - return (*VariableDeclaration)(unsafe.Pointer(n)).ExportableData() - case 2: - return (*BindingElement)(unsafe.Pointer(n)).ExportableData() - case 3: - return (*FunctionDeclaration)(unsafe.Pointer(n)).ExportableData() - case 4: - return (*ClassDeclaration)(unsafe.Pointer(n)).ExportableData() - case 5: - return (*ClassExpression)(unsafe.Pointer(n)).ExportableData() - case 6: - return (*InterfaceDeclaration)(unsafe.Pointer(n)).ExportableData() - case 7: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).ExportableData() - case 8: - return (*EnumDeclaration)(unsafe.Pointer(n)).ExportableData() - case 9: - return (*NamespaceImport)(unsafe.Pointer(n)).ExportableData() - case 10: - return (*ExportSpecifier)(unsafe.Pointer(n)).ExportableData() - case 11: - return (*ModuleDeclaration)(unsafe.Pointer(n)).ExportableData() - case 12: - return (*ImportEqualsDeclaration)(unsafe.Pointer(n)).ExportableData() - case 13: - return (*ImportClause)(unsafe.Pointer(n)).ExportableData() - case 14: - return (*ImportSpecifier)(unsafe.Pointer(n)).ExportableData() - default: - return nil + if offset := nodeExportableDataDispatch[n.Kind]; offset != 0 { + return (*ExportableBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeLocalsContainerDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindForStatement: 1, - KindForInStatement: 2, - KindForOfStatement: 2, - KindCaseBlock: 3, - KindCatchClause: 4, - KindBlock: 5, - KindFunctionDeclaration: 6, - KindClassDeclaration: 7, - KindClassExpression: 8, - KindTypeAliasDeclaration: 9, - KindJSTypeAliasDeclaration: 9, - KindCallSignature: 10, - KindConstructSignature: 11, - KindConstructor: 12, - KindGetAccessor: 13, - KindSetAccessor: 14, - KindIndexSignature: 15, - KindMethodSignature: 16, - KindMethodDeclaration: 17, - KindClassStaticBlockDeclaration: 18, - KindArrowFunction: 19, - KindFunctionExpression: 20, - KindConditionalType: 21, - KindMappedType: 22, - KindFunctionType: 23, - KindConstructorType: 24, - KindJSDocSignature: 25, - KindSourceFile: 26, - KindModuleDeclaration: 27, + KindForStatement: uint8(unsafe.Offsetof(ForStatement{}.LocalsContainerBase)), + KindForInStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.LocalsContainerBase)), + KindForOfStatement: uint8(unsafe.Offsetof(ForInOrOfStatement{}.LocalsContainerBase)), + KindCaseBlock: uint8(unsafe.Offsetof(CaseBlock{}.LocalsContainerBase)), + KindCatchClause: uint8(unsafe.Offsetof(CatchClause{}.LocalsContainerBase)), + KindBlock: uint8(unsafe.Offsetof(Block{}.LocalsContainerBase)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.LocalsContainerBase)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.LocalsContainerBase)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.LocalsContainerBase)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.LocalsContainerBase)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.LocalsContainerBase)), + KindCallSignature: uint8(unsafe.Offsetof(CallSignatureDeclaration{}.LocalsContainerBase)), + KindConstructSignature: uint8(unsafe.Offsetof(ConstructSignatureDeclaration{}.LocalsContainerBase)), + KindConstructor: uint8(unsafe.Offsetof(ConstructorDeclaration{}.LocalsContainerBase)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.LocalsContainerBase)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.LocalsContainerBase)), + KindIndexSignature: uint8(unsafe.Offsetof(IndexSignatureDeclaration{}.LocalsContainerBase)), + KindMethodSignature: uint8(unsafe.Offsetof(MethodSignatureDeclaration{}.LocalsContainerBase)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.LocalsContainerBase)), + KindClassStaticBlockDeclaration: uint8(unsafe.Offsetof(ClassStaticBlockDeclaration{}.LocalsContainerBase)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.LocalsContainerBase)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.LocalsContainerBase)), + KindConditionalType: uint8(unsafe.Offsetof(ConditionalTypeNode{}.LocalsContainerBase)), + KindMappedType: uint8(unsafe.Offsetof(MappedTypeNode{}.LocalsContainerBase)), + KindFunctionType: uint8(unsafe.Offsetof(FunctionTypeNode{}.LocalsContainerBase)), + KindConstructorType: uint8(unsafe.Offsetof(ConstructorTypeNode{}.LocalsContainerBase)), + KindJSDocSignature: uint8(unsafe.Offsetof(JSDocSignature{}.LocalsContainerBase)), + KindSourceFile: uint8(unsafe.Offsetof(SourceFile{}.LocalsContainerBase)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.LocalsContainerBase)), } func (n *Node) LocalsContainerData() *LocalsContainerBase { - switch nodeLocalsContainerDataDispatch[n.Kind] { - case 1: - return (*ForStatement)(unsafe.Pointer(n)).LocalsContainerData() - case 2: - return (*ForInOrOfStatement)(unsafe.Pointer(n)).LocalsContainerData() - case 3: - return (*CaseBlock)(unsafe.Pointer(n)).LocalsContainerData() - case 4: - return (*CatchClause)(unsafe.Pointer(n)).LocalsContainerData() - case 5: - return (*Block)(unsafe.Pointer(n)).LocalsContainerData() - case 6: - return (*FunctionDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 7: - return (*ClassDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 8: - return (*ClassExpression)(unsafe.Pointer(n)).LocalsContainerData() - case 9: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 10: - return (*CallSignatureDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 11: - return (*ConstructSignatureDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 12: - return (*ConstructorDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 13: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 14: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 15: - return (*IndexSignatureDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 16: - return (*MethodSignatureDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 17: - return (*MethodDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 18: - return (*ClassStaticBlockDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - case 19: - return (*ArrowFunction)(unsafe.Pointer(n)).LocalsContainerData() - case 20: - return (*FunctionExpression)(unsafe.Pointer(n)).LocalsContainerData() - case 21: - return (*ConditionalTypeNode)(unsafe.Pointer(n)).LocalsContainerData() - case 22: - return (*MappedTypeNode)(unsafe.Pointer(n)).LocalsContainerData() - case 23: - return (*FunctionTypeNode)(unsafe.Pointer(n)).LocalsContainerData() - case 24: - return (*ConstructorTypeNode)(unsafe.Pointer(n)).LocalsContainerData() - case 25: - return (*JSDocSignature)(unsafe.Pointer(n)).LocalsContainerData() - case 26: - return (*SourceFile)(unsafe.Pointer(n)).LocalsContainerData() - case 27: - return (*ModuleDeclaration)(unsafe.Pointer(n)).LocalsContainerData() - default: - return nil + if offset := nodeLocalsContainerDataDispatch[n.Kind]; offset != 0 { + return (*LocalsContainerBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeFunctionLikeDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindFunctionDeclaration: 1, - KindCallSignature: 2, - KindConstructSignature: 3, - KindConstructor: 4, - KindGetAccessor: 5, - KindSetAccessor: 6, - KindIndexSignature: 7, - KindMethodSignature: 8, - KindMethodDeclaration: 9, - KindArrowFunction: 10, - KindFunctionExpression: 11, - KindFunctionType: 12, - KindConstructorType: 13, - KindJSDocSignature: 14, + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.FunctionLikeBase)), + KindCallSignature: uint8(unsafe.Offsetof(CallSignatureDeclaration{}.FunctionLikeBase)), + KindConstructSignature: uint8(unsafe.Offsetof(ConstructSignatureDeclaration{}.FunctionLikeBase)), + KindConstructor: uint8(unsafe.Offsetof(ConstructorDeclaration{}.FunctionLikeBase)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.FunctionLikeBase)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.FunctionLikeBase)), + KindIndexSignature: uint8(unsafe.Offsetof(IndexSignatureDeclaration{}.FunctionLikeBase)), + KindMethodSignature: uint8(unsafe.Offsetof(MethodSignatureDeclaration{}.FunctionLikeBase)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.FunctionLikeBase)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.FunctionLikeBase)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.FunctionLikeBase)), + KindFunctionType: uint8(unsafe.Offsetof(FunctionTypeNode{}.FunctionLikeBase)), + KindConstructorType: uint8(unsafe.Offsetof(ConstructorTypeNode{}.FunctionLikeBase)), + KindJSDocSignature: uint8(unsafe.Offsetof(JSDocSignature{}.FunctionLikeBase)), } func (n *Node) FunctionLikeData() *FunctionLikeBase { - switch nodeFunctionLikeDataDispatch[n.Kind] { - case 1: - return (*FunctionDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 2: - return (*CallSignatureDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 3: - return (*ConstructSignatureDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 4: - return (*ConstructorDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 5: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 6: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 7: - return (*IndexSignatureDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 8: - return (*MethodSignatureDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 9: - return (*MethodDeclaration)(unsafe.Pointer(n)).FunctionLikeData() - case 10: - return (*ArrowFunction)(unsafe.Pointer(n)).FunctionLikeData() - case 11: - return (*FunctionExpression)(unsafe.Pointer(n)).FunctionLikeData() - case 12: - return (*FunctionTypeNode)(unsafe.Pointer(n)).FunctionLikeData() - case 13: - return (*ConstructorTypeNode)(unsafe.Pointer(n)).FunctionLikeData() - case 14: - return (*JSDocSignature)(unsafe.Pointer(n)).FunctionLikeData() - default: - return nil + if offset := nodeFunctionLikeDataDispatch[n.Kind]; offset != 0 { + return (*FunctionLikeBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil +} + +var nodeClassLikeDataDispatch = [kindFlowReduceLabelData + 1]uint8{ + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.ClassLikeBase)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.ClassLikeBase)), } func (n *Node) ClassLikeData() *ClassLikeBase { - switch n.Kind { - case KindClassDeclaration: - return (*ClassDeclaration)(unsafe.Pointer(n)).ClassLikeData() - case KindClassExpression: - return (*ClassExpression)(unsafe.Pointer(n)).ClassLikeData() - default: - return nil + if offset := nodeClassLikeDataDispatch[n.Kind]; offset != 0 { + return (*ClassLikeBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeBodyDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindFunctionDeclaration: 1, - KindConstructor: 2, - KindGetAccessor: 3, - KindSetAccessor: 4, - KindMethodDeclaration: 5, - KindArrowFunction: 6, - KindFunctionExpression: 7, - KindModuleDeclaration: 8, + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.BodyBase)), + KindConstructor: uint8(unsafe.Offsetof(ConstructorDeclaration{}.BodyBase)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.BodyBase)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.BodyBase)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.BodyBase)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.BodyBase)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.BodyBase)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.BodyBase)), } func (n *Node) BodyData() *BodyBase { - switch nodeBodyDataDispatch[n.Kind] { - case 1: - return (*FunctionDeclaration)(unsafe.Pointer(n)).BodyData() - case 2: - return (*ConstructorDeclaration)(unsafe.Pointer(n)).BodyData() - case 3: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).BodyData() - case 4: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).BodyData() - case 5: - return (*MethodDeclaration)(unsafe.Pointer(n)).BodyData() - case 6: - return (*ArrowFunction)(unsafe.Pointer(n)).BodyData() - case 7: - return (*FunctionExpression)(unsafe.Pointer(n)).BodyData() - case 8: - return (*ModuleDeclaration)(unsafe.Pointer(n)).BodyData() - default: - return nil + if offset := nodeBodyDataDispatch[n.Kind]; offset != 0 { + return (*BodyBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeLiteralLikeDataDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindStringLiteral: 1, - KindNumericLiteral: 2, - KindBigIntLiteral: 3, - KindRegularExpressionLiteral: 4, - KindNoSubstitutionTemplateLiteral: 5, - KindTemplateHead: 6, - KindTemplateMiddle: 7, - KindTemplateTail: 8, - KindJsxText: 9, + KindStringLiteral: uint8(unsafe.Offsetof(StringLiteral{}.LiteralLikeNodeBase)), + KindNumericLiteral: uint8(unsafe.Offsetof(NumericLiteral{}.LiteralLikeNodeBase)), + KindBigIntLiteral: uint8(unsafe.Offsetof(BigIntLiteral{}.LiteralLikeNodeBase)), + KindRegularExpressionLiteral: uint8(unsafe.Offsetof(RegularExpressionLiteral{}.LiteralLikeNodeBase)), + KindNoSubstitutionTemplateLiteral: uint8(unsafe.Offsetof(NoSubstitutionTemplateLiteral{}.LiteralLikeNodeBase)), + KindTemplateHead: uint8(unsafe.Offsetof(TemplateHead{}.LiteralLikeNodeBase)), + KindTemplateMiddle: uint8(unsafe.Offsetof(TemplateMiddle{}.LiteralLikeNodeBase)), + KindTemplateTail: uint8(unsafe.Offsetof(TemplateTail{}.LiteralLikeNodeBase)), + KindJsxText: uint8(unsafe.Offsetof(JsxText{}.LiteralLikeNodeBase)), } func (n *Node) LiteralLikeData() *LiteralLikeNodeBase { - switch nodeLiteralLikeDataDispatch[n.Kind] { - case 1: - return (*StringLiteral)(unsafe.Pointer(n)).LiteralLikeData() - case 2: - return (*NumericLiteral)(unsafe.Pointer(n)).LiteralLikeData() - case 3: - return (*BigIntLiteral)(unsafe.Pointer(n)).LiteralLikeData() - case 4: - return (*RegularExpressionLiteral)(unsafe.Pointer(n)).LiteralLikeData() - case 5: - return (*NoSubstitutionTemplateLiteral)(unsafe.Pointer(n)).LiteralLikeData() - case 6: - return (*TemplateHead)(unsafe.Pointer(n)).LiteralLikeData() - case 7: - return (*TemplateMiddle)(unsafe.Pointer(n)).LiteralLikeData() - case 8: - return (*TemplateTail)(unsafe.Pointer(n)).LiteralLikeData() - case 9: - return (*JsxText)(unsafe.Pointer(n)).LiteralLikeData() - default: - return nil + if offset := nodeLiteralLikeDataDispatch[n.Kind]; offset != 0 { + return (*LiteralLikeNodeBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil +} + +var nodeTemplateLiteralLikeDataDispatch = [kindFlowReduceLabelData + 1]uint8{ + KindNoSubstitutionTemplateLiteral: uint8(unsafe.Offsetof(NoSubstitutionTemplateLiteral{}.TemplateLiteralLikeNodeBase)), + KindTemplateHead: uint8(unsafe.Offsetof(TemplateHead{}.TemplateLiteralLikeNodeBase)), + KindTemplateMiddle: uint8(unsafe.Offsetof(TemplateMiddle{}.TemplateLiteralLikeNodeBase)), + KindTemplateTail: uint8(unsafe.Offsetof(TemplateTail{}.TemplateLiteralLikeNodeBase)), } func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { - switch n.Kind { - case KindNoSubstitutionTemplateLiteral: - return (*NoSubstitutionTemplateLiteral)(unsafe.Pointer(n)).TemplateLiteralLikeData() - case KindTemplateHead: - return (*TemplateHead)(unsafe.Pointer(n)).TemplateLiteralLikeData() - case KindTemplateMiddle: - return (*TemplateMiddle)(unsafe.Pointer(n)).TemplateLiteralLikeData() - case KindTemplateTail: - return (*TemplateTail)(unsafe.Pointer(n)).TemplateLiteralLikeData() - default: - return nil + if offset := nodeTemplateLiteralLikeDataDispatch[n.Kind]; offset != 0 { + return (*TemplateLiteralLikeNodeBase)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } // ────────────────────────────────────────────────────────────────────── @@ -11662,309 +11344,104 @@ func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { // ────────────────────────────────────────────────────────────────────── var nodeNameDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindVariableDeclaration: 1, - KindParameter: 2, - KindBindingElement: 3, - KindFunctionDeclaration: 4, - KindClassDeclaration: 5, - KindClassExpression: 6, - KindInterfaceDeclaration: 7, - KindTypeAliasDeclaration: 8, - KindJSTypeAliasDeclaration: 8, - KindEnumMember: 9, - KindEnumDeclaration: 10, - KindNamespaceImport: 11, - KindNamespaceExportDeclaration: 12, - KindNamespaceExport: 13, - KindExportSpecifier: 14, - KindGetAccessor: 15, - KindSetAccessor: 16, - KindMethodSignature: 17, - KindMethodDeclaration: 18, - KindPropertySignature: 19, - KindPropertyDeclaration: 20, - KindFunctionExpression: 21, - KindPropertyAccessExpression: 22, - KindMetaProperty: 23, - KindPropertyAssignment: 24, - KindShorthandPropertyAssignment: 25, - KindImportAttribute: 26, - KindNamedTupleMember: 27, - KindJsxNamespacedName: 28, - KindJsxAttribute: 29, - KindJSDocCallbackTag: 30, - KindJSDocTypedefTag: 31, - KindJSDocNameReference: 32, - KindModuleDeclaration: 33, - KindImportEqualsDeclaration: 34, - KindImportClause: 35, - KindImportSpecifier: 36, - KindJSDocLink: 37, - KindJSDocLinkPlain: 38, - KindJSDocLinkCode: 39, - KindTypeParameter: 40, - KindJSDocParameterTag: 41, - KindJSDocPropertyTag: 41, + KindVariableDeclaration: uint8(unsafe.Offsetof(VariableDeclaration{}.name)), + KindParameter: uint8(unsafe.Offsetof(ParameterDeclaration{}.name)), + KindBindingElement: uint8(unsafe.Offsetof(BindingElement{}.name)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.name)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.name)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.name)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.name)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.name)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.name)), + KindEnumMember: uint8(unsafe.Offsetof(EnumMember{}.name)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.name)), + KindNamespaceImport: uint8(unsafe.Offsetof(NamespaceImport{}.name)), + KindNamespaceExportDeclaration: uint8(unsafe.Offsetof(NamespaceExportDeclaration{}.name)), + KindNamespaceExport: uint8(unsafe.Offsetof(NamespaceExport{}.name)), + KindExportSpecifier: uint8(unsafe.Offsetof(ExportSpecifier{}.name)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.name)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.name)), + KindMethodSignature: uint8(unsafe.Offsetof(MethodSignatureDeclaration{}.name)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.name)), + KindPropertySignature: uint8(unsafe.Offsetof(PropertySignatureDeclaration{}.name)), + KindPropertyDeclaration: uint8(unsafe.Offsetof(PropertyDeclaration{}.name)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.name)), + KindPropertyAccessExpression: uint8(unsafe.Offsetof(PropertyAccessExpression{}.name)), + KindMetaProperty: uint8(unsafe.Offsetof(MetaProperty{}.name)), + KindPropertyAssignment: uint8(unsafe.Offsetof(PropertyAssignment{}.name)), + KindShorthandPropertyAssignment: uint8(unsafe.Offsetof(ShorthandPropertyAssignment{}.name)), + KindImportAttribute: uint8(unsafe.Offsetof(ImportAttribute{}.name)), + KindNamedTupleMember: uint8(unsafe.Offsetof(NamedTupleMember{}.name)), + KindJsxNamespacedName: uint8(unsafe.Offsetof(JsxNamespacedName{}.name)), + KindJsxAttribute: uint8(unsafe.Offsetof(JsxAttribute{}.name)), + KindJSDocCallbackTag: uint8(unsafe.Offsetof(JSDocCallbackTag{}.name)), + KindJSDocTypedefTag: uint8(unsafe.Offsetof(JSDocTypedefTag{}.name)), + KindJSDocNameReference: uint8(unsafe.Offsetof(JSDocNameReference{}.name)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.name)), + KindImportEqualsDeclaration: uint8(unsafe.Offsetof(ImportEqualsDeclaration{}.name)), + KindImportClause: uint8(unsafe.Offsetof(ImportClause{}.name)), + KindImportSpecifier: uint8(unsafe.Offsetof(ImportSpecifier{}.name)), + KindJSDocLink: uint8(unsafe.Offsetof(JSDocLink{}.name)), + KindJSDocLinkPlain: uint8(unsafe.Offsetof(JSDocLinkPlain{}.name)), + KindJSDocLinkCode: uint8(unsafe.Offsetof(JSDocLinkCode{}.name)), + KindTypeParameter: uint8(unsafe.Offsetof(TypeParameterDeclaration{}.name)), + KindJSDocParameterTag: uint8(unsafe.Offsetof(JSDocParameterOrPropertyTag{}.name)), + KindJSDocPropertyTag: uint8(unsafe.Offsetof(JSDocParameterOrPropertyTag{}.name)), } func (n *Node) Name() *DeclarationName { - switch nodeNameDispatch[n.Kind] { - case 1: - return (*VariableDeclaration)(unsafe.Pointer(n)).Name() - case 2: - return (*ParameterDeclaration)(unsafe.Pointer(n)).Name() - case 3: - return (*BindingElement)(unsafe.Pointer(n)).Name() - case 4: - return (*FunctionDeclaration)(unsafe.Pointer(n)).Name() - case 5: - return (*ClassDeclaration)(unsafe.Pointer(n)).Name() - case 6: - return (*ClassExpression)(unsafe.Pointer(n)).Name() - case 7: - return (*InterfaceDeclaration)(unsafe.Pointer(n)).Name() - case 8: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).Name() - case 9: - return (*EnumMember)(unsafe.Pointer(n)).Name() - case 10: - return (*EnumDeclaration)(unsafe.Pointer(n)).Name() - case 11: - return (*NamespaceImport)(unsafe.Pointer(n)).Name() - case 12: - return (*NamespaceExportDeclaration)(unsafe.Pointer(n)).Name() - case 13: - return (*NamespaceExport)(unsafe.Pointer(n)).Name() - case 14: - return (*ExportSpecifier)(unsafe.Pointer(n)).Name() - case 15: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).Name() - case 16: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).Name() - case 17: - return (*MethodSignatureDeclaration)(unsafe.Pointer(n)).Name() - case 18: - return (*MethodDeclaration)(unsafe.Pointer(n)).Name() - case 19: - return (*PropertySignatureDeclaration)(unsafe.Pointer(n)).Name() - case 20: - return (*PropertyDeclaration)(unsafe.Pointer(n)).Name() - case 21: - return (*FunctionExpression)(unsafe.Pointer(n)).Name() - case 22: - return (*PropertyAccessExpression)(unsafe.Pointer(n)).Name() - case 23: - return (*MetaProperty)(unsafe.Pointer(n)).Name() - case 24: - return (*PropertyAssignment)(unsafe.Pointer(n)).Name() - case 25: - return (*ShorthandPropertyAssignment)(unsafe.Pointer(n)).Name() - case 26: - return (*ImportAttribute)(unsafe.Pointer(n)).Name() - case 27: - return (*NamedTupleMember)(unsafe.Pointer(n)).Name() - case 28: - return (*JsxNamespacedName)(unsafe.Pointer(n)).Name() - case 29: - return (*JsxAttribute)(unsafe.Pointer(n)).Name() - case 30: - return (*JSDocCallbackTag)(unsafe.Pointer(n)).Name() - case 31: - return (*JSDocTypedefTag)(unsafe.Pointer(n)).Name() - case 32: - return (*JSDocNameReference)(unsafe.Pointer(n)).Name() - case 33: - return (*ModuleDeclaration)(unsafe.Pointer(n)).Name() - case 34: - return (*ImportEqualsDeclaration)(unsafe.Pointer(n)).Name() - case 35: - return (*ImportClause)(unsafe.Pointer(n)).Name() - case 36: - return (*ImportSpecifier)(unsafe.Pointer(n)).Name() - case 37: - return (*JSDocLink)(unsafe.Pointer(n)).Name() - case 38: - return (*JSDocLinkPlain)(unsafe.Pointer(n)).Name() - case 39: - return (*JSDocLinkCode)(unsafe.Pointer(n)).Name() - case 40: - return (*TypeParameterDeclaration)(unsafe.Pointer(n)).Name() - case 41: - return (*JSDocParameterOrPropertyTag)(unsafe.Pointer(n)).Name() - default: - return nil + if offset := nodeNameDispatch[n.Kind]; offset != 0 { + return *(**DeclarationName)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } var nodeModifiersDispatch = [kindFlowReduceLabelData + 1]uint8{ - KindVariableStatement: 1, - KindParameter: 2, - KindMissingDeclaration: 3, - KindFunctionDeclaration: 4, - KindClassDeclaration: 5, - KindClassExpression: 6, - KindInterfaceDeclaration: 7, - KindTypeAliasDeclaration: 8, - KindJSTypeAliasDeclaration: 8, - KindEnumDeclaration: 9, - KindImportDeclaration: 10, - KindJSImportDeclaration: 10, - KindExportAssignment: 11, - KindNamespaceExportDeclaration: 12, - KindConstructor: 13, - KindGetAccessor: 14, - KindSetAccessor: 15, - KindIndexSignature: 16, - KindMethodSignature: 17, - KindMethodDeclaration: 18, - KindPropertySignature: 19, - KindPropertyDeclaration: 20, - KindClassStaticBlockDeclaration: 21, - KindBinaryExpression: 22, - KindArrowFunction: 23, - KindFunctionExpression: 24, - KindPropertyAssignment: 25, - KindShorthandPropertyAssignment: 26, - KindConstructorType: 27, - KindModuleDeclaration: 28, - KindImportEqualsDeclaration: 29, - KindExportDeclaration: 30, - KindTypeParameter: 31, + KindVariableStatement: uint8(unsafe.Offsetof(VariableStatement{}.modifiers)), + KindParameter: uint8(unsafe.Offsetof(ParameterDeclaration{}.modifiers)), + KindMissingDeclaration: uint8(unsafe.Offsetof(MissingDeclaration{}.modifiers)), + KindFunctionDeclaration: uint8(unsafe.Offsetof(FunctionDeclaration{}.modifiers)), + KindClassDeclaration: uint8(unsafe.Offsetof(ClassDeclaration{}.modifiers)), + KindClassExpression: uint8(unsafe.Offsetof(ClassExpression{}.modifiers)), + KindInterfaceDeclaration: uint8(unsafe.Offsetof(InterfaceDeclaration{}.modifiers)), + KindTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.modifiers)), + KindJSTypeAliasDeclaration: uint8(unsafe.Offsetof(TypeAliasDeclaration{}.modifiers)), + KindEnumDeclaration: uint8(unsafe.Offsetof(EnumDeclaration{}.modifiers)), + KindImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.modifiers)), + KindJSImportDeclaration: uint8(unsafe.Offsetof(ImportDeclaration{}.modifiers)), + KindExportAssignment: uint8(unsafe.Offsetof(ExportAssignment{}.modifiers)), + KindNamespaceExportDeclaration: uint8(unsafe.Offsetof(NamespaceExportDeclaration{}.modifiers)), + KindConstructor: uint8(unsafe.Offsetof(ConstructorDeclaration{}.modifiers)), + KindGetAccessor: uint8(unsafe.Offsetof(GetAccessorDeclaration{}.modifiers)), + KindSetAccessor: uint8(unsafe.Offsetof(SetAccessorDeclaration{}.modifiers)), + KindIndexSignature: uint8(unsafe.Offsetof(IndexSignatureDeclaration{}.modifiers)), + KindMethodSignature: uint8(unsafe.Offsetof(MethodSignatureDeclaration{}.modifiers)), + KindMethodDeclaration: uint8(unsafe.Offsetof(MethodDeclaration{}.modifiers)), + KindPropertySignature: uint8(unsafe.Offsetof(PropertySignatureDeclaration{}.modifiers)), + KindPropertyDeclaration: uint8(unsafe.Offsetof(PropertyDeclaration{}.modifiers)), + KindClassStaticBlockDeclaration: uint8(unsafe.Offsetof(ClassStaticBlockDeclaration{}.modifiers)), + KindBinaryExpression: uint8(unsafe.Offsetof(BinaryExpression{}.modifiers)), + KindArrowFunction: uint8(unsafe.Offsetof(ArrowFunction{}.modifiers)), + KindFunctionExpression: uint8(unsafe.Offsetof(FunctionExpression{}.modifiers)), + KindPropertyAssignment: uint8(unsafe.Offsetof(PropertyAssignment{}.modifiers)), + KindShorthandPropertyAssignment: uint8(unsafe.Offsetof(ShorthandPropertyAssignment{}.modifiers)), + KindConstructorType: uint8(unsafe.Offsetof(ConstructorTypeNode{}.modifiers)), + KindModuleDeclaration: uint8(unsafe.Offsetof(ModuleDeclaration{}.modifiers)), + KindImportEqualsDeclaration: uint8(unsafe.Offsetof(ImportEqualsDeclaration{}.modifiers)), + KindExportDeclaration: uint8(unsafe.Offsetof(ExportDeclaration{}.modifiers)), + KindTypeParameter: uint8(unsafe.Offsetof(TypeParameterDeclaration{}.modifiers)), } func (n *Node) Modifiers() *ModifierList { - switch nodeModifiersDispatch[n.Kind] { - case 1: - return (*VariableStatement)(unsafe.Pointer(n)).Modifiers() - case 2: - return (*ParameterDeclaration)(unsafe.Pointer(n)).Modifiers() - case 3: - return (*MissingDeclaration)(unsafe.Pointer(n)).Modifiers() - case 4: - return (*FunctionDeclaration)(unsafe.Pointer(n)).Modifiers() - case 5: - return (*ClassDeclaration)(unsafe.Pointer(n)).Modifiers() - case 6: - return (*ClassExpression)(unsafe.Pointer(n)).Modifiers() - case 7: - return (*InterfaceDeclaration)(unsafe.Pointer(n)).Modifiers() - case 8: - return (*TypeAliasDeclaration)(unsafe.Pointer(n)).Modifiers() - case 9: - return (*EnumDeclaration)(unsafe.Pointer(n)).Modifiers() - case 10: - return (*ImportDeclaration)(unsafe.Pointer(n)).Modifiers() - case 11: - return (*ExportAssignment)(unsafe.Pointer(n)).Modifiers() - case 12: - return (*NamespaceExportDeclaration)(unsafe.Pointer(n)).Modifiers() - case 13: - return (*ConstructorDeclaration)(unsafe.Pointer(n)).Modifiers() - case 14: - return (*GetAccessorDeclaration)(unsafe.Pointer(n)).Modifiers() - case 15: - return (*SetAccessorDeclaration)(unsafe.Pointer(n)).Modifiers() - case 16: - return (*IndexSignatureDeclaration)(unsafe.Pointer(n)).Modifiers() - case 17: - return (*MethodSignatureDeclaration)(unsafe.Pointer(n)).Modifiers() - case 18: - return (*MethodDeclaration)(unsafe.Pointer(n)).Modifiers() - case 19: - return (*PropertySignatureDeclaration)(unsafe.Pointer(n)).Modifiers() - case 20: - return (*PropertyDeclaration)(unsafe.Pointer(n)).Modifiers() - case 21: - return (*ClassStaticBlockDeclaration)(unsafe.Pointer(n)).Modifiers() - case 22: - return (*BinaryExpression)(unsafe.Pointer(n)).Modifiers() - case 23: - return (*ArrowFunction)(unsafe.Pointer(n)).Modifiers() - case 24: - return (*FunctionExpression)(unsafe.Pointer(n)).Modifiers() - case 25: - return (*PropertyAssignment)(unsafe.Pointer(n)).Modifiers() - case 26: - return (*ShorthandPropertyAssignment)(unsafe.Pointer(n)).Modifiers() - case 27: - return (*ConstructorTypeNode)(unsafe.Pointer(n)).Modifiers() - case 28: - return (*ModuleDeclaration)(unsafe.Pointer(n)).Modifiers() - case 29: - return (*ImportEqualsDeclaration)(unsafe.Pointer(n)).Modifiers() - case 30: - return (*ExportDeclaration)(unsafe.Pointer(n)).Modifiers() - case 31: - return (*TypeParameterDeclaration)(unsafe.Pointer(n)).Modifiers() - default: - return nil + if offset := nodeModifiersDispatch[n.Kind]; offset != 0 { + return *(**ModifierList)(unsafe.Add(unsafe.Pointer(n), offset)) } + return nil } func (n *MutableNode) SetModifiers(modifiers *ModifierList) { - switch n.Kind { - case KindVariableStatement: - (*VariableStatement)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindParameter: - (*ParameterDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindMissingDeclaration: - (*MissingDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindFunctionDeclaration: - (*FunctionDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindClassDeclaration: - (*ClassDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindClassExpression: - (*ClassExpression)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindInterfaceDeclaration: - (*InterfaceDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: - (*TypeAliasDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindEnumDeclaration: - (*EnumDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindImportDeclaration, KindJSImportDeclaration: - (*ImportDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindExportAssignment: - (*ExportAssignment)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindNamespaceExportDeclaration: - (*NamespaceExportDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindConstructor: - (*ConstructorDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindGetAccessor: - (*GetAccessorDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindSetAccessor: - (*SetAccessorDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindIndexSignature: - (*IndexSignatureDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindMethodSignature: - (*MethodSignatureDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindMethodDeclaration: - (*MethodDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindPropertySignature: - (*PropertySignatureDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindPropertyDeclaration: - (*PropertyDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindClassStaticBlockDeclaration: - (*ClassStaticBlockDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindBinaryExpression: - (*BinaryExpression)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindArrowFunction: - (*ArrowFunction)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindFunctionExpression: - (*FunctionExpression)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindPropertyAssignment: - (*PropertyAssignment)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindShorthandPropertyAssignment: - (*ShorthandPropertyAssignment)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindConstructorType: - (*ConstructorTypeNode)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindModuleDeclaration: - (*ModuleDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindImportEqualsDeclaration: - (*ImportEqualsDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindExportDeclaration: - (*ExportDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) - case KindTypeParameter: - (*TypeParameterDeclaration)(unsafe.Pointer(n)).setModifiers(modifiers) + if offset := nodeModifiersDispatch[n.Kind]; offset != 0 { + *(**ModifierList)(unsafe.Add(unsafe.Pointer(n), offset)) = modifiers } } diff --git a/tsc/internal/ast/flow.go b/tsc/internal/ast/flow.go index 43a4bc2f0dfea..9238e49b32cd5 100644 --- a/tsc/internal/ast/flow.go +++ b/tsc/internal/ast/flow.go @@ -27,10 +27,17 @@ const ( // FlowNode type FlowNode struct { - Flags FlowFlags - Node *Node // Associated AST node - Antecedent *FlowNode // Antecedent for all but FlowLabel - Antecedents *FlowList // Linked list of antecedents for FlowLabel + Flags FlowFlags + Node *Node // Associated AST node + Antecedent *FlowNode // Antecedent for all but FlowLabel; for FlowLabel, stores *FlowList +} + +func (f *FlowNode) Antecedents() *FlowList { + return (*FlowList)(unsafe.Pointer(f.Antecedent)) +} + +func (f *FlowNode) SetAntecedents(list *FlowList) { + f.Antecedent = (*FlowNode)(unsafe.Pointer(list)) } type FlowList struct { diff --git a/tsc/internal/ast/lazylist.go b/tsc/internal/ast/lazylist.go new file mode 100644 index 0000000000000..cef7a3eae14b7 --- /dev/null +++ b/tsc/internal/ast/lazylist.go @@ -0,0 +1,600 @@ +package ast + +import ( + "sync" + "sync/atomic" + "unsafe" + + "github.com/microsoft/TypeScript/tsc/internal/collections" + "github.com/microsoft/TypeScript/tsc/internal/core" +) + +const ( + LazyListPending uint32 = 0 // Parsed lazily; binder has not visited owner yet + LazyListDeferred uint32 = 1 // Binder visited owner and deferred binding + LazyListUnbound uint32 = 2 // File finished binding without visiting owner + LazyListDone uint32 = 3 // Forced (parsed and bound if deferred) + + LazyParsingContextTypeAliasRHS uint8 = 255 +) + +var ( + lazySentinelElem *Node + lazySentinelNodes = unsafe.Slice(&lazySentinelElem, 1)[:0] +) + +type LazyBindContext struct { + Container *Node + ThisContainer *Node + BlockScopeContainer *Node + CurrentFlow *FlowNode + UnreachableFlow *FlowNode +} + +type LazyNodeList struct { + NodeList // Must be at offset 0 so (*LazyNodeList)(unsafe.Pointer(list)) is valid + Placeholder Node + state atomic.Uint32 + ParsingContext uint8 + ContextFlags NodeFlags + ParsingContexts uint32 + Owner *Node + File *SourceFile + BindCtx *LazyBindContext + forcedNodes []*Node + nestedLazyLists []*LazyNodeList + mu sync.Mutex +} + +type savedMemberSymbolState struct { + sym *Symbol + declHeader uint32 + declPtr unsafe.Pointer + valueDecl *Node +} + +type forcedSymbolState struct { + owner *Symbol + prevExtra *symbolExtra + savedMembers []savedMemberSymbolState +} + +var ( + lazyReparseHook func(lazy *LazyNodeList, file *SourceFile, allowNestedLazy bool) []*Node + lazyLookupHook func(lazy *LazyNodeList, file *SourceFile, name string) *Node + lazyBindHook func(lazy *LazyNodeList, file *SourceFile, nodes []*Node) + lazySymbolLocks [256]sync.Mutex + trackForcedLazy atomic.Bool + forcedLazyMu sync.Mutex + forcedLazyLists []*LazyNodeList + forcedLazySymbols []forcedSymbolState +) + +func SetTrackForcedLazy(enabled bool) { + trackForcedLazy.Store(enabled) +} + +func ResetForcedLazyLists() { + forcedLazyMu.Lock() + defer forcedLazyMu.Unlock() + for i := len(forcedLazySymbols) - 1; i >= 0; i-- { + entry := forcedLazySymbols[i] + for _, sm := range entry.savedMembers { + md := &(*SymbolWithData)(unsafe.Pointer(sm.sym)).d + sm.sym.declHeader = sm.declHeader + md.declPtr = sm.declPtr + md.valueDeclaration = sm.valueDecl + } + d := &(*SymbolWithData)(unsafe.Pointer(entry.owner)).d + d.extra = entry.prevExtra + entry.owner.SetHasLazyMembers() + } + forcedLazySymbols = nil + for i := len(forcedLazyLists) - 1; i >= 0; i-- { + l := forcedLazyLists[i] + l.forcedNodes = nil + l.nestedLazyLists = nil + if l.BindCtx != nil { + l.state.Store(LazyListDeferred) + } else { + l.state.Store(LazyListUnbound) + } + } + forcedLazyLists = nil + for i := range subtreeFactsShards { + subtreeFactsShards[i].m = nil + } + setParentInChildrenPool = sync.Pool{ + New: func() any { + return newParentInChildrenSetter() + }, + } +} + +func (node *SourceFile) ResetWasmCaches() { + node.jsdocCache = nil + node.identifiersOnce = sync.Once{} + node.identifiers = collections.Set[string]{} + node.declarationMap = nil + node.nameTableOnce = sync.Once{} + node.nameTable = nil + node.positionMapOnce = sync.Once{} + node.positionMap = nil + node.ecmaLineMap = nil + node.tokenCache = nil + node.tokenFactory = nil +} + +func SetLazyParseHook(fn func(lazy *LazyNodeList, file *SourceFile, allowNestedLazy bool) []*Node) { + lazyReparseHook = fn +} + +func SetLazyBindHook(fn func(lazy *LazyNodeList, file *SourceFile, nodes []*Node)) { + lazyBindHook = fn +} + +func SetLazyLookupHook(fn func(lazy *LazyNodeList, file *SourceFile, name string) *Node) { + lazyLookupHook = fn +} + +func NewLazyNodeList(loc core.TextRange, parsingContext uint8, contextFlags NodeFlags, parsingContexts uint32) *LazyNodeList { + var list LazyNodeList + list.Loc = loc + list.Nodes = lazySentinelNodes + list.ParsingContext = parsingContext + list.ContextFlags = contextFlags + list.ParsingContexts = parsingContexts + return &list +} + +func NewLazyTypeAliasRHS(loc core.TextRange, contextFlags NodeFlags, parsingContexts uint32) *LazyNodeList { + var list LazyNodeList + list.Loc = loc + list.Nodes = lazySentinelNodes + list.ParsingContext = LazyParsingContextTypeAliasRHS + list.ContextFlags = contextFlags + list.ParsingContexts = parsingContexts + list.Placeholder.Loc = loc + list.Placeholder.Kind = KindUnknown + list.Placeholder.Parent = (*Node)(unsafe.Pointer(&list)) + return &list +} + +func (n *Node) IsLazyTypeNode() bool { + return n != nil && n.Kind == KindUnknown && n.Parent != nil +} + +func (n *Node) AsLazyTypeNode() *LazyNodeList { + return (*LazyNodeList)(unsafe.Pointer(n.Parent)) +} + +//go:noinline +func (n *Node) forceLazyTypeNode() *Node { + if n.Parent == nil { + return nil + } + nodes := (*LazyNodeList)(unsafe.Pointer(n.Parent)).Force() + if len(nodes) > 0 { + return nodes[0] + } + return nil +} + +func SetLazyTypeOwner(typeNode *Node, owner *Node) { + if typeNode.IsLazyTypeNode() { + typeNode.AsLazyTypeNode().Owner = owner + } +} + +func (list *NodeList) IsLazy() bool { + return list != nil && unsafe.SliceData(list.Nodes) == &lazySentinelElem +} + +func (list *NodeList) AsLazy() *LazyNodeList { + return (*LazyNodeList)(unsafe.Pointer(list)) +} + +func (list *NodeList) Slice() []*Node { + if list == nil { + return nil + } + if unsafe.SliceData(list.Nodes) == &lazySentinelElem { + return list.forceLazy() + } + return list.Nodes +} + +//go:noinline +func (list *NodeList) forceLazy() []*Node { + return (*LazyNodeList)(unsafe.Pointer(list)).Force() +} + +func SetLazyOwner(list *NodeList, owner *Node) { + if list != nil && unsafe.SliceData(list.Nodes) == &lazySentinelElem { + (*LazyNodeList)(unsafe.Pointer(list)).Owner = owner + } +} + +func (lazy *LazyNodeList) State() uint32 { + return lazy.state.Load() +} + +func (lazy *LazyNodeList) SetNestedLazyLists(lists []*LazyNodeList) { + lazy.nestedLazyLists = lists +} + +func (lazy *LazyNodeList) NestedLazyLists() []*LazyNodeList { + return lazy.nestedLazyLists +} + +func (lazy *LazyNodeList) Defer(file *SourceFile, ctx *LazyBindContext) { + lazy.File = file + lazy.BindCtx = ctx + lazy.state.Store(LazyListDeferred) +} + +func (lazy *LazyNodeList) MarkUnbound(file *SourceFile) { + if lazy.state.Load() == LazyListPending { + lazy.File = file + lazy.state.Store(LazyListUnbound) + } +} + +func (lazy *LazyNodeList) ParseNow(file *SourceFile) []*Node { + lazy.File = file + nodes := lazyReparseHook(lazy, file, true /*allowNestedLazy*/) + lazy.forcedNodes = nodes + lazy.state.Store(LazyListDone) + return nodes +} + +func (lazy *LazyNodeList) Force() []*Node { + switch lazy.state.Load() { + case LazyListDone: + return lazy.forcedNodes + case LazyListPending: + return nil + } + if lazy.Owner != nil { + if sym := lazy.Owner.Symbol(); sym != nil && atomic.LoadUint32(&sym.owner().declHeader)&lazyStateMask != 0 { + sym.forceLazyMembers() + if lazy.state.Load() == LazyListDone { + return lazy.forcedNodes + } + } + } + lazy.mu.Lock() + defer lazy.mu.Unlock() + if lazy.state.Load() == LazyListDone { + return lazy.forcedNodes + } + if lazy.state.Load() == LazyListPending { + return nil + } + wasDeferred := lazy.state.Load() == LazyListDeferred + nodes := lazyReparseHook(lazy, lazy.File, wasDeferred) + lazy.forcedNodes = nodes + if wasDeferred && lazy.BindCtx != nil { + lazyBindHook(lazy, lazy.File, nodes) + } + for _, nested := range lazy.nestedLazyLists { + nested.MarkUnbound(lazy.File) + } + lazy.nestedLazyLists = nil + if trackForcedLazy.Load() { + forcedLazyMu.Lock() + forcedLazyLists = append(forcedLazyLists, lazy) + forcedLazyMu.Unlock() + } else { + lazy.BindCtx = nil + } + lazy.state.Store(LazyListDone) + return nodes +} + +func (s *Symbol) SetHasLazyMembers() { + owner := s.owner() + for { + old := atomic.LoadUint32(&owner.declHeader) + next := (old &^ lazyStateMask) | lazyStatePending + if atomic.CompareAndSwapUint32(&owner.declHeader, old, next) { + break + } + } +} + +func (s *Symbol) HasLazyMembers() bool { + return s != nil && atomic.LoadUint32(&s.owner().declHeader)&lazyStateMask == lazyStatePending +} + +//go:noinline +func (s *Symbol) membersSlow() SymbolTable { + if atomic.LoadUint32(&s.owner().declHeader)&lazyStateMask != 0 { + s.forceLazyMembers() + } + d := s.data() + if d.extra == nil { + return nil + } + return d.extra.members +} + +//go:noinline +func (s *Symbol) exportsSlow() SymbolTable { + if atomic.LoadUint32(&s.owner().declHeader)&lazyStateMask != 0 { + s.forceLazyMembers() + } + d := s.data() + if d.extra == nil { + return nil + } + return d.extra.exports +} + +//go:noinline +func memberCountHint(decl *Node) int { + list := decl.MemberList() + if list == nil { + return 0 + } + if list.IsLazy() { + return len(list.AsLazy().forcedNodes) + } + return len(list.Nodes) +} + +func (s *Symbol) getMembersSlow(arena *SymbolExtraArena) SymbolTable { + if atomic.LoadUint32(&s.owner().declHeader)&lazyStateMask == lazyStatePending { + s.forceLazyMembers() + } + m := &s.ensureExtra(arena).members + if *m == nil { + var capHint int + if decls := s.Declarations(); len(decls) > 0 && decls[0] != nil { + switch decls[0].Kind { + case KindInterfaceDeclaration, KindClassDeclaration, KindClassExpression, KindTypeLiteral: + capHint = memberCountHint(decls[0]) + case KindObjectLiteralExpression, KindJsxAttributes: + capHint = len(decls[0].Properties()) + } + } + *m = NewSymbolTableWithCapacity(capHint) + } + return *m +} + +//go:noinline +func (s *Symbol) getExportsSlow(arena *SymbolExtraArena) SymbolTable { + if atomic.LoadUint32(&s.owner().declHeader)&lazyStateMask == lazyStatePending { + s.forceLazyMembers() + } + m := &s.ensureExtra(arena).exports + if *m == nil { + var capHint int + if decls := s.Declarations(); len(decls) > 0 && decls[0] != nil { + switch decls[0].Kind { + case KindEnumDeclaration: + capHint = memberCountHint(decls[0]) + case KindSourceFile: + capHint = len(decls[0].Statements()) + case KindModuleDeclaration: + if body := decls[0].Body(); body != nil && body.Kind == KindModuleBlock { + capHint = len(body.Statements()) + } + } + } + *m = NewSymbolTableWithCapacity(capHint) + } + return *m +} + +func saveTableMemberStates(t SymbolTable, dst []savedMemberSymbolState) []savedMemberSymbolState { + for _, m := range t.All() { + if m != nil { + mo := m.owner() + md := &(*SymbolWithData)(unsafe.Pointer(mo)).d + dst = append(dst, savedMemberSymbolState{ + sym: mo, + declHeader: mo.declHeader, + declPtr: md.declPtr, + valueDecl: md.valueDeclaration, + }) + } + } + return dst +} + +func (s *Symbol) forceLazyMembers() { + if s == nil { + return + } + owner := s.owner() + if atomic.LoadUint32(&owner.declHeader)&lazyStateMask == 0 { + return + } + mu := &lazySymbolLocks[(uintptr(unsafe.Pointer(owner))>>6)&255] + mu.Lock() + defer mu.Unlock() + if atomic.LoadUint32(&owner.declHeader)&lazyStateMask != lazyStatePending { + return + } + tracking := trackForcedLazy.Load() + if tracking { + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + prevExtra := d.extra + var savedMembers []savedMemberSymbolState + if prevExtra != nil { + cloned := *prevExtra + if cloned.members != nil { + savedMembers = saveTableMemberStates(cloned.members, savedMembers) + cloned.members = cloned.members.Clone() + } + if cloned.exports != nil { + savedMembers = saveTableMemberStates(cloned.exports, savedMembers) + cloned.exports = cloned.exports.Clone() + } + d.extra = &cloned + } + forcedLazyMu.Lock() + forcedLazySymbols = append(forcedLazySymbols, forcedSymbolState{ + owner: owner, + prevExtra: prevExtra, + savedMembers: savedMembers, + }) + forcedLazyMu.Unlock() + } + for { + old := atomic.LoadUint32(&owner.declHeader) + if atomic.CompareAndSwapUint32(&owner.declHeader, old, (old&^lazyStateMask)|lazyStateActive) { + break + } + } + for _, decl := range s.Declarations() { + if decl == nil { + continue + } + switch decl.Kind { + case KindInterfaceDeclaration, KindClassDeclaration, KindClassExpression, KindTypeLiteral: + list := decl.MemberList() + if list.IsLazy() { + l := list.AsLazy() + if l.state.Load() == LazyListDeferred { + nodes := lazyReparseHook(l, l.File, true /*allowNestedLazy*/) + l.forcedNodes = nodes + lazyBindHook(l, l.File, nodes) + for _, nested := range l.nestedLazyLists { + nested.MarkUnbound(l.File) + } + l.nestedLazyLists = nil + if tracking { + forcedLazyMu.Lock() + forcedLazyLists = append(forcedLazyLists, l) + forcedLazyMu.Unlock() + } else { + l.BindCtx = nil + } + l.state.Store(LazyListDone) + } + } + } + } + for { + old := atomic.LoadUint32(&owner.declHeader) + if atomic.CompareAndSwapUint32(&owner.declHeader, old, old&^lazyStateMask) { + break + } + } +} + +func (lazy *LazyNodeList) IsBinaryKeyedTable() bool { + if lazy == nil || lazy.Loc.Len() <= 8192 || lazy.File == nil { + return false + } + text := lazy.File.Text()[lazy.Loc.Pos():lazy.Loc.End()] + if len(text) < 8 || text[0] != '\n' { + return false + } + i := 1 + for i < len(text) && i < 16 && text[i] == ' ' { + i++ + } + if i == 1 || i >= len(text) || text[i] != '\'' { + return false + } + kStart := i + 1 + kEnd := kStart + for kEnd < len(text) && kEnd < kStart+64 && (text[kEnd] == '0' || text[kEnd] == '1') { + kEnd++ + } + return kEnd > kStart && kEnd+2 <= len(text) && text[kEnd] == '\'' && text[kEnd+1] == ':' +} + +func (s *Symbol) IsBinaryKeyedTypeLiteral() bool { + if s == nil || !s.HasLazyMembers() { + return false + } + decls := s.Declarations() + if len(decls) != 1 || decls[0] == nil || decls[0].Kind != KindTypeLiteral { + return false + } + list := decls[0].MemberList() + return list != nil && list.IsLazy() && list.AsLazy().IsBinaryKeyedTable() +} + +func (s *Symbol) LookupLazyMember(name string) *Symbol { + if s == nil { + return nil + } + owner := s.owner() + mu := &lazySymbolLocks[(uintptr(unsafe.Pointer(owner))>>6)&255] + mu.Lock() + defer mu.Unlock() + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + if atomic.LoadUint32(&owner.declHeader)&lazyStateMask != lazyStatePending { + if d.extra == nil { + return nil + } + return d.extra.members.Get(name) + } + if d.extra != nil && d.extra.members != nil { + if sym, ok := d.extra.members.Lookup(name); ok { + return sym + } + } + decls := s.Declarations() + if len(decls) != 1 || decls[0] == nil || decls[0].Kind != KindTypeLiteral { + return nil + } + list := decls[0].MemberList() + if list == nil || !list.IsLazy() { + return nil + } + l := list.AsLazy() + if l.state.Load() != LazyListDeferred || lazyLookupHook == nil { + return nil + } + node := lazyLookupHook(l, l.File, name) + if node == nil { + return nil + } + if trackForcedLazy.Load() && (d.extra == nil || d.extra.members == nil) { + prevExtra := d.extra + var savedMembers []savedMemberSymbolState + if prevExtra != nil { + cloned := *prevExtra + if cloned.members != nil { + savedMembers = saveTableMemberStates(cloned.members, savedMembers) + cloned.members = cloned.members.Clone() + } + if cloned.exports != nil { + savedMembers = saveTableMemberStates(cloned.exports, savedMembers) + cloned.exports = cloned.exports.Clone() + } + d.extra = &cloned + } + forcedLazyMu.Lock() + forcedLazySymbols = append(forcedLazySymbols, forcedSymbolState{ + owner: owner, + prevExtra: prevExtra, + savedMembers: savedMembers, + }) + forcedLazyMu.Unlock() + } + for { + old := atomic.LoadUint32(&owner.declHeader) + if atomic.CompareAndSwapUint32(&owner.declHeader, old, (old&^lazyStateMask)|lazyStateActive) { + break + } + } + lazyBindHook(l, l.File, []*Node{node}) + for { + old := atomic.LoadUint32(&owner.declHeader) + if atomic.CompareAndSwapUint32(&owner.declHeader, old, (old&^lazyStateMask)|lazyStatePending) { + break + } + } + if d.extra == nil { + return nil + } + return d.extra.members.Get(name) +} diff --git a/tsc/internal/ast/nodeaccessors_test.go b/tsc/internal/ast/nodeaccessors_test.go index c18342076dbdb..f842ed1fc2f89 100644 --- a/tsc/internal/ast/nodeaccessors_test.go +++ b/tsc/internal/ast/nodeaccessors_test.go @@ -81,11 +81,11 @@ func TestNodeAccessorsSharedKinds(t *testing.T) { factory := ast.NewNodeFactory(ast.NodeFactoryHooks{}) name := factory.NewIdentifier("T") modifiers := factory.NewModifierList([]*ast.Node{factory.NewToken(ast.KindExportKeyword)}) - node := factory.NewTypeAliasDeclaration(modifiers, name, nil, nil) - data := node.AsTypeAliasDeclaration() - - for _, kind := range []ast.Kind{ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration} { - node.Kind = kind + for _, node := range []*ast.Node{ + factory.NewTypeAliasDeclaration(modifiers, name, nil, nil), + factory.NewJSTypeAliasDeclaration(modifiers, name, nil, nil), + } { + data := node.AsTypeAliasDeclaration() assert.Equal(t, node.Name(), name) assert.Equal(t, node.Modifiers(), modifiers) assert.Equal(t, node.DeclarationData(), &data.DeclarationBase) diff --git a/tsc/internal/ast/nodeflags.go b/tsc/internal/ast/nodeflags.go index 2c808601a2260..cd86f5679a440 100644 --- a/tsc/internal/ast/nodeflags.go +++ b/tsc/internal/ast/nodeflags.go @@ -45,6 +45,9 @@ const ( NodeFlagsPossiblyContainsDeprecatedTag NodeFlags = 1 << 26 // Set during parse if comment text contains '@deprecated'; must confirm via JSDoc lookup NodeFlagsUnreachable NodeFlags = 1 << 27 // If node is unreachable according to the binder NodeFlagsReparserTransformedLiteral NodeFlags = 1 << 28 // If node was transformed during parsing, making its' naive text source not match the AST + // Set by the checker (getConditionalFlowTypeOfType) to memoize that no ancestor of this node, + // up to the enclosing statement, can contribute an implied constraint. + NodeFlagsCondFlowClean NodeFlags = 1 << 29 NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing diff --git a/tsc/internal/ast/subtreefacts.go b/tsc/internal/ast/subtreefacts.go index 7c6a2a5cc113a..ff3efe41625fc 100644 --- a/tsc/internal/ast/subtreefacts.go +++ b/tsc/internal/ast/subtreefacts.go @@ -119,7 +119,7 @@ func propagateNodeListSubtreeFacts(children *NodeList, propagate func(*Node) Sub return SubtreeFactsNone } facts := SubtreeFactsNone - for _, child := range children.Nodes { + for _, child := range children.Slice() { facts |= propagate(child) } return facts diff --git a/tsc/internal/ast/symbol.go b/tsc/internal/ast/symbol.go index 00312310691c5..d66da65ceb362 100644 --- a/tsc/internal/ast/symbol.go +++ b/tsc/internal/ast/symbol.go @@ -1,33 +1,35 @@ package ast import ( + "iter" + "slices" "strings" "sync/atomic" + "unsafe" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/debug" ) -// Symbol stores flags, checkFlags, and id uniquely for every symbol instance, but may share -// symbolData with other symbols. This is done so that instantiated symbols in the checker can -// all share the same underlying symbolData. Every Symbol needs its data field initialized either -// through SymbolWithData.Initialize or by explicitly setting it with Symbol.SetSymbolData. +// Symbol stores flags, checkFlags, id, and declaration header uniquely for every symbol instance. +// Ordinary symbols are embedded at offset 0 of SymbolWithData and store their parent in parentOrData; +// transient symbols created with SetSymbolData set checkFlagsSharedData and store their owner Symbol +// in parentOrData. type Symbol struct { - flags SymbolFlags - checkFlags CheckFlags // Non-zero only in transient symbols created by Checker - id atomic.Uint64 - data *symbolData + flags SymbolFlags + checkFlags CheckFlags // Non-zero only in transient symbols created by Checker + id atomic.Uint32 + declHeader uint32 // bits 0..14: declLen, bits 15..29: declCap, bits 30..31: lazyState + parentOrData *Symbol } // Every symbol references a symbolData structure that may be shared with other symbols. type symbolData struct { name string - declarations []*Node + declPtr unsafe.Pointer valueDeclaration *Node - parent *Symbol extra *symbolExtra } - type symbolExtra struct { members SymbolTable exports SymbolTable @@ -40,62 +42,214 @@ type SymbolExtraArena struct { arena core.Arena[symbolExtra] } -func (s *Symbol) Flags() SymbolFlags { return s.flags } -func (s *Symbol) CheckFlags() CheckFlags { return s.checkFlags } -func (s *Symbol) Name() string { return s.data.name } -func (s *Symbol) Declarations() []*Node { return s.data.declarations } -func (s *Symbol) ValueDeclaration() *Node { return s.data.valueDeclaration } -func (s *Symbol) Parent() *Symbol { return s.data.parent } +const ( + checkFlagsMerged CheckFlags = 1 << 27 // Symbol is a key in Checker.mergedSymbols + checkFlagsReferencedAny CheckFlags = 1 << 28 + checkFlagsReferencedVariable CheckFlags = 1 << 29 + checkFlagsReferencedTypeParameter CheckFlags = 1 << 30 + checkFlagsSharedData CheckFlags = 1 << 31 + checkFlagsReferencedMask CheckFlags = checkFlagsReferencedAny | checkFlagsReferencedVariable | checkFlagsReferencedTypeParameter + checkFlagsInternalMask CheckFlags = checkFlagsMerged | checkFlagsReferencedMask | checkFlagsSharedData + declLenCapMask uint32 = 0x7fff + declCapShift uint32 = 15 + lazyStateShift uint32 = 30 + lazyStateMask uint32 = 3 << lazyStateShift + lazyStatePending uint32 = 1 << lazyStateShift + lazyStateActive uint32 = 2 << lazyStateShift +) -func (s *Symbol) Members() SymbolTable { - if s.data.extra == nil { +func (s *Symbol) owner() *Symbol { + if s.checkFlags&checkFlagsSharedData != 0 { + return s.parentOrData + } + return s +} + +func (s *Symbol) data() *symbolData { + if s.checkFlags&checkFlagsSharedData != 0 { + return &(*SymbolWithData)(unsafe.Pointer(s.parentOrData)).d + } + return &(*SymbolWithData)(unsafe.Pointer(s)).d +} + +func (s *Symbol) Flags() SymbolFlags { return s.flags } +func (s *Symbol) CheckFlags() CheckFlags { + return CheckFlags(atomic.LoadUint32((*uint32)(&s.checkFlags))) &^ checkFlagsInternalMask +} + +func (s *Symbol) AddReference(meaning SymbolFlags) { + if s == nil { + return + } + flags := uint32(checkFlagsReferencedAny) + if meaning&SymbolFlagsVariable != 0 { + flags |= uint32(checkFlagsReferencedVariable) + } + if meaning&SymbolFlagsTypeParameter != 0 { + flags |= uint32(checkFlagsReferencedTypeParameter) + } + for { + old := atomic.LoadUint32((*uint32)(&s.checkFlags)) + if old&flags == flags || atomic.CompareAndSwapUint32((*uint32)(&s.checkFlags), old, old|flags) { + break + } + } +} + +func (s *Symbol) ReferenceKinds() SymbolFlags { + if s == nil { + return 0 + } + cf := CheckFlags(atomic.LoadUint32((*uint32)(&s.checkFlags))) + if cf&checkFlagsReferencedMask == 0 { + return 0 + } + res := SymbolFlagsAll &^ (SymbolFlagsVariable | SymbolFlagsTypeParameter) + if cf&checkFlagsReferencedVariable != 0 { + res |= SymbolFlagsVariable + } + if cf&checkFlagsReferencedTypeParameter != 0 { + res |= SymbolFlagsTypeParameter + } + return res +} + +func (s *Symbol) IsMerged() bool { + return CheckFlags(atomic.LoadUint32((*uint32)(&s.checkFlags)))&checkFlagsMerged != 0 +} + +func (s *Symbol) MarkMerged() { + for { + old := atomic.LoadUint32((*uint32)(&s.checkFlags)) + if CheckFlags(old)&checkFlagsMerged != 0 { + return + } + if atomic.CompareAndSwapUint32((*uint32)(&s.checkFlags), old, old|uint32(checkFlagsMerged)) { + return + } + } +} +func (s *Symbol) Name() string { return s.data().name } +func (s *Symbol) Declarations() []*Node { + owner := s.owner() + hdr := owner.declHeader + declLen := hdr & declLenCapMask + if declLen == 0 { return nil } - return s.data.extra.members + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + declCap := (hdr >> declCapShift) & declLenCapMask + if declCap == 0 { + return unsafe.Slice((**Node)(unsafe.Pointer(&d.declPtr)), 1) + } + return unsafe.Slice((**Node)(d.declPtr), declCap)[:declLen] } -func (s *Symbol) Exports() SymbolTable { - if s.data.extra == nil { +func (s *Symbol) FirstDeclaration() *Node { + owner := s.owner() + hdr := owner.declHeader + if hdr&declLenCapMask == 0 { return nil } - return s.data.extra.exports + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + if (hdr>>declCapShift)&declLenCapMask == 0 { + return *(**Node)(unsafe.Pointer(&d.declPtr)) + } + return *(**Node)(d.declPtr) +} +func (s *Symbol) ValueDeclaration() *Node { return s.data().valueDeclaration } +func (s *Symbol) Parent() *Symbol { return s.owner().parentOrData } + +func (s *Symbol) Members() SymbolTable { + if s.declHeader < lazyStatePending { + if extra := (*SymbolWithData)(unsafe.Pointer(s)).d.extra; extra != nil { + return extra.members + } + } + return s.membersSlow() +} + +func (s *Symbol) Exports() SymbolTable { + if s.declHeader < lazyStatePending { + if extra := (*SymbolWithData)(unsafe.Pointer(s)).d.extra; extra != nil { + return extra.exports + } + } + return s.exportsSlow() } func (s *Symbol) ExportSymbol() *Symbol { - if s.data.extra == nil { + d := s.data() + if d.extra == nil { return nil } - return s.data.extra.exportSymbol + return d.extra.exportSymbol } -func (s *Symbol) SetFlags(value SymbolFlags) { s.flags = value } -func (s *Symbol) SetCheckFlags(value CheckFlags) { s.checkFlags = value } -func (s *Symbol) SetName(value string) { s.data.name = value } -func (s *Symbol) SetDeclarations(value []*Node) { s.data.declarations = value } -func (s *Symbol) SetValueDeclaration(value *Node) { s.data.valueDeclaration = value } -func (s *Symbol) SetParent(value *Symbol) { s.data.parent = value } +func (s *Symbol) SetFlags(value SymbolFlags) { s.flags = value } +func (s *Symbol) SetCheckFlags(value CheckFlags) { + for { + old := atomic.LoadUint32((*uint32)(&s.checkFlags)) + next := (old & uint32(checkFlagsInternalMask)) | (uint32(value) &^ uint32(checkFlagsInternalMask)) + if atomic.CompareAndSwapUint32((*uint32)(&s.checkFlags), old, next) { + break + } + } +} +func (s *Symbol) SetName(value string) { s.data().name = value } +func (s *Symbol) SetSingleDeclaration(node *Node) { + owner := s.owner() + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + if node != nil { + d.declPtr = unsafe.Add(unsafe.Pointer(&node.Flags), -int(unsafe.Offsetof(Node{}.Flags))) + } else { + d.declPtr = nil + } + owner.declHeader = (owner.declHeader & lazyStateMask) | 1 +} + +func (s *Symbol) SetDeclarations(value []*Node) { + owner := s.owner() + d := &(*SymbolWithData)(unsafe.Pointer(owner)).d + lazy := owner.declHeader & lazyStateMask + if len(value) == 0 { + d.declPtr = nil + owner.declHeader = lazy + return + } + d.declPtr = unsafe.Pointer(unsafe.SliceData(value)) + declLen := uint32(len(value)) & declLenCapMask + declCap := uint32(max(cap(value), len(value))) & declLenCapMask + owner.declHeader = lazy | declLen | (declCap << declCapShift) +} +func (s *Symbol) SetValueDeclaration(value *Node) { s.data().valueDeclaration = value } +func (s *Symbol) SetParent(value *Symbol) { s.owner().parentOrData = value } func (s *Symbol) ensureExtra(arena *SymbolExtraArena) *symbolExtra { - if s.data.extra == nil { - s.data.extra = arena.arena.New() + d := s.data() + if d.extra == nil { + d.extra = arena.arena.New() } - return s.data.extra + return d.extra } func (s *Symbol) SetMembers(value SymbolTable, arena *SymbolExtraArena) { - if value != nil || s.data.extra != nil { + d := s.data() + if value != nil || d.extra != nil { s.ensureExtra(arena).members = value } } func (s *Symbol) SetExports(value SymbolTable, arena *SymbolExtraArena) { - if value != nil || s.data.extra != nil { + d := s.data() + if value != nil || d.extra != nil { s.ensureExtra(arena).exports = value } } func (s *Symbol) SetExportSymbol(value *Symbol, arena *SymbolExtraArena) { - if value != nil || s.data.extra != nil { + d := s.data() + if value != nil || d.extra != nil { s.ensureExtra(arena).exportSymbol = value } } @@ -108,7 +262,6 @@ type SymbolWithData struct { // Initializes a SymbolWithData instance and returns the embedded Symbol. func (sd *SymbolWithData) Initialize() *Symbol { - sd.s.data = &sd.d return &sd.s } @@ -117,9 +270,10 @@ func NewSymbol() *Symbol { } // Initializes a Symbol's data field with the data from another Symbol. This allows multiple -// symbols to share the same underlying symbolData. func (s *Symbol) SetSymbolData(other *Symbol) { - s.data = other.data + s.parentOrData = other.owner() + s.checkFlags |= checkFlagsSharedData + s.declHeader = lazyStateMask } // GetSourceFileOfSymbol returns the owning file of a published binder symbol, or @@ -164,9 +318,294 @@ func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags { } // SymbolTable +// +// A SymbolTable maps names to symbols and iterates in insertion order, like Strada's Map, so that iterating a table +// is deterministic and visits symbols in the order the binder and checker added them. A nil SymbolTable is an empty +// table that can be read and iterated but not written, like a nil map. +// +// The entries are kept in insertion order in one slice. Most tables are small, so a table is searched linearly until +// it has more than symbolTableLinearMax entries, and only then gets a hash index of entry positions. An entry does not +// store its key as the key is almost always the symbol's own name. In the rare cases when a symbol has a different name, +// the table stores a proxy symbol with the new name and a reference to the original symbol in the parent field. +// +// A table must not be changed with Set or Delete while it is being iterated. +type SymbolTable = *SymbolMap -type SymbolTable map[string]*Symbol +const symbolTableLinearMax = 16 +type SymbolMap struct { + entries []*Symbol + index []int32 // open addressing table of entry positions plus one (0 is empty), only past symbolTableLinearMax +} + +func hashSymbolName(s string) uint64 { + n := len(s) + if n == 0 { + return 0 + } + p := unsafe.Pointer(unsafe.StringData(s)) + var h uint64 + if n >= 8 { + h = *(*uint64)(p) + for off := 8; off+8 < n; off += 8 { + h = (h ^ *(*uint64)(unsafe.Add(p, off))) * 0x9e3779b97f4a7c15 + } + h ^= *(*uint64)(unsafe.Add(p, n-8)) * 0xbf58476d1ce4e5b9 + } else if n >= 4 { + h = uint64(*(*uint32)(p)) | (uint64(*(*uint32)(unsafe.Add(p, n-4))) << 32) + } else { + h = uint64(*(*byte)(p)) | (uint64(*(*byte)(unsafe.Add(p, n>>1))) << 8) | (uint64(*(*byte)(unsafe.Add(p, n-1))) << 16) + } + h = (h ^ uint64(n)) * 0x94d049bb133111eb + return h ^ (h >> 32) +} + +func NewSymbolTable() SymbolTable { + return NewSymbolTableWithCapacity(0) +} + +func NewSymbolTableWithCapacity(capacity int) SymbolTable { + m := &SymbolMap{} + if capacity > 0 { + m.entries = make([]*Symbol, 0, capacity) + if capacity > symbolTableLinearMax { + m.index = make([]int32, indexSizeFor(capacity)) + } + } + return m +} + +// SymbolTableOf returns a new table holding symbol under name. +func SymbolTableOf(name string, symbol *Symbol) SymbolTable { + m := NewSymbolTableWithCapacity(1) + m.Set(name, symbol) + return m +} + +func (m *SymbolMap) keyAt(i int) string { + return m.entries[i].Name() +} + +func (m *SymbolMap) symbolAt(i int) *Symbol { + symbol := m.entries[i] + if symbol.flags == SymbolFlagsNameProxy { + symbol = symbol.parentOrData + } + return symbol +} + +func (m *SymbolMap) find(key string) int { + if m == nil { + return -1 + } + if m.index != nil { + mask := uint64(len(m.index) - 1) + for slot := hashSymbolName(key) & mask; ; slot = (slot + 1) & mask { + p := m.index[slot] + if p == 0 { + return -1 + } + if i := int(p - 1); m.keyAt(i) == key { + return i + } + } + } + for i := range m.entries { + if m.keyAt(i) == key { + return i + } + } + return -1 +} + +func (m *SymbolMap) Len() int { + if m == nil { + return 0 + } + return len(m.entries) +} + +func (m *SymbolMap) Get(key string) *Symbol { + if i := m.find(key); i >= 0 { + return m.symbolAt(i) + } + return nil +} + +func (m *SymbolMap) Lookup(key string) (*Symbol, bool) { + if i := m.find(key); i >= 0 { + return m.symbolAt(i), true + } + return nil, false +} + +// Set replaces the symbol of an existing key in place, keeping its position, or adds the key at the end. +func (m *SymbolMap) Set(key string, symbol *Symbol) { + if key != symbol.Name() { + proxy := NewSymbol() + proxy.flags = SymbolFlagsNameProxy + proxy.data().name = key + proxy.parentOrData = symbol + symbol = proxy + } + if m.index != nil { + mask := uint64(len(m.index) - 1) + slot := hashSymbolName(key) & mask + for { + p := m.index[slot] + if p == 0 { + break + } + if i := int(p - 1); m.keyAt(i) == key { + m.entries[i] = symbol + return + } + slot = (slot + 1) & mask + } + i := int32(len(m.entries)) + m.entries = append(m.entries, symbol) + if len(m.entries)*4 <= len(m.index)*3 { + m.index[slot] = i + 1 + } else { + m.buildIndex() + } + return + } + for i := range m.entries { + if m.keyAt(i) == key { + m.entries[i] = symbol + return + } + } + if cap(m.entries) == 0 { + // Most tables hold one or two entries, so start with room for two rather than growing from one. + m.entries = make([]*Symbol, 0, 2) + } + m.entries = append(m.entries, symbol) + if len(m.entries) > symbolTableLinearMax { + m.buildIndex() + } +} + +func indexSizeFor(n int) int { + size := 32 + for size*3 < n*4 { + size *= 2 + } + return size +} + +func (m *SymbolMap) insertIndex(key string, i int32) { + mask := uint64(len(m.index) - 1) + slot := hashSymbolName(key) & mask + for m.index[slot] != 0 { + slot = (slot + 1) & mask + } + m.index[slot] = i + 1 +} + +func (m *SymbolMap) buildIndex() { + m.index = make([]int32, indexSizeFor(len(m.entries))) + for i := range m.entries { + m.insertIndex(m.keyAt(i), int32(i)) + } +} + +func (m *SymbolMap) Delete(key string) { + d := m.find(key) + if d < 0 { + return + } + m.entries = slices.Delete(m.entries, d, d+1) + m.index = nil + if len(m.entries) > symbolTableLinearMax { + m.buildIndex() + } +} + +// All iterates over the keys and symbols of the table in insertion order. +func (m *SymbolMap) All() iter.Seq2[string, *Symbol] { + return func(yield func(string, *Symbol) bool) { + if m == nil { + return + } + entries := m.entries + for i := range entries { + if !yield(m.keyAt(i), m.symbolAt(i)) { + return + } + } + } +} + +func (m *SymbolMap) Keys() iter.Seq[string] { + return func(yield func(string) bool) { + for key := range m.All() { + if !yield(key) { + return + } + } + } +} + +func (m *SymbolMap) Values() iter.Seq[*Symbol] { + return func(yield func(*Symbol) bool) { + if m == nil { + return + } + entries := m.entries + for i := range entries { + if !yield(m.symbolAt(i)) { + return + } + } + } +} + +func (m *SymbolMap) Clone() SymbolTable { + if m == nil { + return nil + } + clone := &SymbolMap{entries: slices.Clone(m.entries), index: slices.Clone(m.index)} + return clone +} + +func (m *SymbolMap) Clear() { + if m == nil { + return + } + clear(m.entries) + m.entries = m.entries[:0] + m.index = nil +} + +func (m *SymbolMap) CopyFrom(src SymbolTable) { + if src == nil || len(src.entries) == 0 { + return + } + if len(m.entries) == 0 { + m.entries = append(m.entries, src.entries...) + if src.index != nil { + m.index = slices.Clone(src.index) + } + return + } + for i := range src.entries { + m.Set(src.keyAt(i), src.symbolAt(i)) + } +} + +func (m *SymbolMap) HasNameProxies() bool { + if m == nil { + return false + } + for _, entry := range m.entries { + if entry.flags == SymbolFlagsNameProxy { + return true + } + } + return false +} const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName const ( diff --git a/tsc/internal/ast/symbol_test.go b/tsc/internal/ast/symbol_test.go index 1f8f44aaa66d2..e53922bec62b6 100644 --- a/tsc/internal/ast/symbol_test.go +++ b/tsc/internal/ast/symbol_test.go @@ -38,8 +38,8 @@ func TestSymbolAccessors(t *testing.T) { parent := ast.NewSymbol() exportSymbol := ast.NewSymbol() declarations := []*ast.Node{declaration} - members := ast.SymbolTable{"member": member} - exports := ast.SymbolTable{"export": export} + members := ast.SymbolTableOf("member", member) + exports := ast.SymbolTableOf("export", export) symbol.SetFlags(ast.SymbolFlagsClass | ast.SymbolFlagsTransient) symbol.SetCheckFlags(ast.CheckFlagsReadonly) @@ -56,8 +56,8 @@ func TestSymbolAccessors(t *testing.T) { assert.Equal(t, symbol.Name(), "C") assert.Equal(t, symbol.Declarations()[0], declaration) assert.Equal(t, symbol.ValueDeclaration(), declaration) - assert.Equal(t, symbol.Members()["member"], member) - assert.Equal(t, symbol.Exports()["export"], export) + assert.Equal(t, symbol.Members().Get("member"), member) + assert.Equal(t, symbol.Exports().Get("export"), export) assert.Equal(t, symbol.Parent(), parent) assert.Equal(t, symbol.ExportSymbol(), exportSymbol) @@ -65,10 +65,10 @@ func TestSymbolAccessors(t *testing.T) { assert.Equal(t, symbol.Declarations()[0], otherDeclaration) symbol.Declarations()[0] = declaration assert.Equal(t, declarations[0], declaration) - symbol.Members()["added"] = export - assert.Equal(t, members["added"], export) - symbol.Exports()["added"] = member - assert.Equal(t, exports["added"], member) + symbol.Members().Set("added", export) + assert.Equal(t, members.Get("added"), export) + symbol.Exports().Set("added", member) + assert.Equal(t, exports.Get("added"), member) symbol.SetFlags(ast.SymbolFlagsNone) symbol.SetCheckFlags(0) @@ -99,26 +99,26 @@ func TestSymbolTableInitialization(t *testing.T) { export := ast.NewSymbol() members := ast.GetMembers(symbol, &arena) - members["member"] = member - assert.Equal(t, symbol.Members()["member"], member) - assert.Equal(t, ast.GetMembers(symbol, &arena)["member"], member) + members.Set("member", member) + assert.Equal(t, symbol.Members().Get("member"), member) + assert.Equal(t, ast.GetMembers(symbol, &arena).Get("member"), member) exports := ast.GetExports(symbol, &arena) - exports["export"] = export - assert.Equal(t, symbol.Exports()["export"], export) - assert.Equal(t, ast.GetExports(symbol, &arena)["export"], export) + exports.Set("export", export) + assert.Equal(t, symbol.Exports().Get("export"), export) + assert.Equal(t, ast.GetExports(symbol, &arena).Get("export"), export) symbol.SetMembers(nil, &arena) symbol.SetExports(nil, &arena) assert.Assert(t, ast.GetMembers(symbol, &arena) != nil) assert.Assert(t, ast.GetExports(symbol, &arena) != nil) - assert.Equal(t, len(symbol.Members()), 0) - assert.Equal(t, len(symbol.Exports()), 0) + assert.Equal(t, symbol.Members().Len(), 0) + assert.Equal(t, symbol.Exports().Len(), 0) } func TestSymbolHotRecordLayout(t *testing.T) { t.Parallel() - assert.Equal(t, unsafe.Sizeof(ast.SymbolWithData{}), unsafe.Sizeof(ast.Symbol{})+8*unsafe.Sizeof((*ast.Node)(nil))) + assert.Equal(t, unsafe.Sizeof(ast.SymbolWithData{}), unsafe.Sizeof(ast.Symbol{})+5*unsafe.Sizeof((*ast.Node)(nil))) } func TestSymbolNilExtraFieldsDoNotAllocate(t *testing.T) { //nolint:paralleltest @@ -130,7 +130,7 @@ func TestSymbolNilExtraFieldsDoNotAllocate(t *testing.T) { //nolint:paralleltest symbol.SetExportSymbol(nil, &arena) }) assert.Equal(t, allocs, float64(0)) - assert.Assert(t, reflect.ValueOf(symbol).Elem().FieldByName("data").Elem().FieldByName("extra").IsNil()) + assert.Assert(t, symbol.Members() == nil && symbol.Exports() == nil && symbol.ExportSymbol() == nil) } func TestSymbolSharedExtraData(t *testing.T) { @@ -142,12 +142,12 @@ func TestSymbolSharedExtraData(t *testing.T) { alias.SetSymbolData(symbol) member := ast.NewSymbol() export := ast.NewSymbol() - alias.SetMembers(ast.SymbolTable{"member": member}, &arena) - symbol.SetExports(ast.SymbolTable{"export": export}, &otherArena) + alias.SetMembers(ast.SymbolTableOf("member", member), &arena) + symbol.SetExports(ast.SymbolTableOf("export", export), &otherArena) alias.SetExportSymbol(export, &otherArena) runtime.GC() - assert.Equal(t, symbol.Members()["member"], member) - assert.Equal(t, alias.Exports()["export"], export) + assert.Equal(t, symbol.Members().Get("member"), member) + assert.Equal(t, alias.Exports().Get("export"), export) assert.Equal(t, symbol.ExportSymbol(), export) symbol.SetMembers(nil, &otherArena) alias.SetExports(nil, &arena) @@ -155,14 +155,14 @@ func TestSymbolSharedExtraData(t *testing.T) { assert.Assert(t, alias.Members() == nil) assert.Assert(t, symbol.Exports() == nil) assert.Assert(t, alias.ExportSymbol() == nil) - ast.GetMembers(alias, &otherArena)["new"] = export - ast.GetExports(symbol, &arena)["new"] = member + ast.GetMembers(alias, &otherArena).Set("new", export) + ast.GetExports(symbol, &arena).Set("new", member) for range 1024 { - ast.NewSymbol().SetExports(ast.SymbolTable{"export": export}, &arena) + ast.NewSymbol().SetExports(ast.SymbolTableOf("export", export), &arena) } arena = ast.SymbolExtraArena{} otherArena = ast.SymbolExtraArena{} runtime.GC() - assert.Equal(t, symbol.Members()["new"], export) - assert.Equal(t, alias.Exports()["new"], member) + assert.Equal(t, symbol.Members().Get("new"), export) + assert.Equal(t, alias.Exports().Get("new"), member) } diff --git a/tsc/internal/ast/symbolflags.go b/tsc/internal/ast/symbolflags.go index 0a66285a13ab1..ca528c7693ab3 100644 --- a/tsc/internal/ast/symbolflags.go +++ b/tsc/internal/ast/symbolflags.go @@ -36,8 +36,9 @@ const ( SymbolFlagsModuleExports SymbolFlags = 1 << 27 // Symbol for CommonJS `module` of `module.exports` SymbolFlagsConstEnumOnlyModule SymbolFlags = 1 << 28 // Module contains only const enums or other modules with only const enums SymbolFlagsReplaceableByMethod SymbolFlags = 1 << 29 - SymbolFlagsGlobalLookup SymbolFlags = 1 << 30 // Flag to signal this is a global lookup - SymbolFlagsAll SymbolFlags = 1<<30 - 1 // All flags except SymbolFlagsGlobalLookup + SymbolFlagsGlobalLookup SymbolFlags = 1 << 30 // Flag to signal this is a global lookup (internal to NameResolver) + SymbolFlagsNameProxy SymbolFlags = 1 << 30 // Renamed symbol proxy (internal to symbol tables) + SymbolFlagsAll SymbolFlags = 1<<30 - 1 // All flags except SymbolFlagsGlobalLookup/NameProxy SymbolFlagsEnum = SymbolFlagsRegularEnum | SymbolFlagsConstEnum SymbolFlagsVariable = SymbolFlagsFunctionScopedVariable | SymbolFlagsBlockScopedVariable diff --git a/tsc/internal/ast/utilities.go b/tsc/internal/ast/utilities.go index ff11fd6a2c2f4..7be789f5b7ef4 100644 --- a/tsc/internal/ast/utilities.go +++ b/tsc/internal/ast/utilities.go @@ -15,10 +15,10 @@ import ( // Atomic ids var ( - nextNodeId atomic.Uint64 - nextSymbolId atomic.Uint64 - nextNodeBlockId atomic.Uint64 - nextSymbolBlockId atomic.Uint64 + nextNodeId atomic.Uint32 + nextSymbolId atomic.Uint32 + nextNodeBlockId atomic.Uint32 + nextSymbolBlockId atomic.Uint32 ) func GetNodeId(node *Node) NodeId { @@ -45,24 +45,32 @@ func GetSymbolId(symbol *Symbol) SymbolId { return SymbolId(id) } +func PeekNodeId(node *Node) NodeId { + return NodeId(node.id.Load()) +} + +func PeekSymbolId(symbol *Symbol) SymbolId { + return SymbolId(symbol.id.Load()) +} + const ( - BlockIdOffset = 0x1_0000_0000_0000 - BlockIdSize = core.LinkPageSize + BlockIdOffset = 0x4000_0000 + BlockIdSize = 1024 ) type NodeIdGenerator struct { - nextId uint64 - lastId uint64 + nextId uint32 + lastId uint32 } func (a *NodeIdGenerator) GetNodeId(node *Node) NodeId { id := node.id.Load() if id == 0 { if a.nextId == a.lastId { - a.nextId = nextNodeBlockId.Add(BlockIdSize) - BlockIdSize + a.nextId = nextNodeBlockId.Add(BlockIdSize) + (BlockIdOffset - BlockIdSize) a.lastId = a.nextId + BlockIdSize } - id = a.nextId + BlockIdOffset + id = a.nextId a.nextId++ if !node.id.CompareAndSwap(0, id) { id = node.id.Load() @@ -72,18 +80,18 @@ func (a *NodeIdGenerator) GetNodeId(node *Node) NodeId { } type SymbolIdGenerator struct { - nextId uint64 - lastId uint64 + nextId uint32 + lastId uint32 } func (a *SymbolIdGenerator) GetSymbolId(symbol *Symbol) SymbolId { id := symbol.id.Load() if id == 0 { if a.nextId == a.lastId { - a.nextId = nextSymbolBlockId.Add(BlockIdSize) - BlockIdSize + a.nextId = nextSymbolBlockId.Add(BlockIdSize) + (BlockIdOffset - BlockIdSize) a.lastId = a.nextId + BlockIdSize } - id = a.nextId + BlockIdOffset + id = a.nextId a.nextId++ if !symbol.id.CompareAndSwap(0, id) { id = symbol.id.Load() @@ -94,21 +102,53 @@ func (a *SymbolIdGenerator) GetSymbolId(symbol *Symbol) SymbolId { func GetSymbolTable(data *SymbolTable) SymbolTable { if *data == nil { - *data = make(SymbolTable) + *data = NewSymbolTable() } return *data } func GetMembers(symbol *Symbol, arena *SymbolExtraArena) SymbolTable { - return GetSymbolTable(&symbol.ensureExtra(arena).members) + owner := symbol.owner() + d := owner.data() + if owner.declHeader&lazyStateMask == 0 && d.extra != nil && d.extra.members != nil { + return d.extra.members + } + return symbol.getMembersSlow(arena) } func GetExports(symbol *Symbol, arena *SymbolExtraArena) SymbolTable { - return GetSymbolTable(&symbol.ensureExtra(arena).exports) + owner := symbol.owner() + d := owner.data() + if owner.declHeader&lazyStateMask == 0 && d.extra != nil && d.extra.exports != nil { + return d.extra.exports + } + return symbol.getExportsSlow(arena) } func GetLocals(container *Node) SymbolTable { - return GetSymbolTable(&container.LocalsContainerData().Locals) + data := container.LocalsContainerData() + if data.Locals == nil { + var capHint int + switch container.Kind { + case KindSourceFile: + capHint = len(container.Statements()) + case KindModuleDeclaration: + if body := container.Body(); body != nil && body.Kind == KindModuleBlock { + capHint = len(body.Statements()) + } + case KindFunctionDeclaration, KindMethodDeclaration, KindMethodSignature, KindConstructor, + KindGetAccessor, KindSetAccessor, KindFunctionExpression, KindArrowFunction, + KindCallSignature, KindConstructSignature, KindFunctionType, KindConstructorType: + capHint = len(container.Parameters()) + if tps := container.TypeParameters(); len(tps) > 0 { + capHint += len(tps) + } + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + capHint = len(container.TypeParameters()) + } + data.Locals = NewSymbolTableWithCapacity(capHint) + } + return data.Locals } // Determines if a node is missing (either `nil` or empty) @@ -1406,15 +1446,15 @@ func IsVoidZero(node *Node) bool { } func IsExportsIdentifier(node *Node) bool { - return IsIdentifier(node) && node.Text() == "exports" + return node != nil && node.Kind == KindIdentifier && node.AsIdentifier().Text == "exports" } func IsModuleIdentifier(node *Node) bool { - return IsIdentifier(node) && node.Text() == "module" + return node != nil && node.Kind == KindIdentifier && node.AsIdentifier().Text == "module" } func IsThisIdentifier(node *Node) bool { - return IsIdentifier(node) && node.Text() == "this" + return node != nil && node.Kind == KindIdentifier && node.AsIdentifier().Text == "this" } func IsThisParameter(node *Node) bool { @@ -2795,13 +2835,34 @@ func GetNodeAtPosition(file *SourceFile, position int, includeJSDoc bool) *Node } } if child == nil { - current.ForEachChild(func(node *Node) bool { - if nodeContainsPosition(node, position) { - child = node - return true + if current.Kind == KindSourceFile { + sf := current.AsSourceFile() + if stmts := sf.Statements.Nodes; len(stmts) > 16 { + lo, hi := 0, len(stmts) + for lo < hi { + mid := int(uint(lo+hi) >> 1) + if stmts[mid].End() <= position { + lo = mid + 1 + } else { + hi = mid + } + } + if lo < len(stmts) && nodeContainsPosition(stmts[lo], position) { + child = stmts[lo] + } else if sf.EndOfFileToken != nil && nodeContainsPosition(sf.EndOfFileToken, position) { + child = sf.EndOfFileToken + } } - return false - }) + } + if child == nil { + current.ForEachChild(func(node *Node) bool { + if nodeContainsPosition(node, position) { + child = node + return true + } + return false + }) + } } if child == nil || IsMetaProperty(child) { return current @@ -2814,30 +2875,26 @@ func nodeContainsPosition(node *Node, position int) bool { return node.Kind >= KindFirstNode && node.Pos() <= position && (position < node.End() || position == node.End() && node.Kind == KindEndOfFile) } -func findImportOrRequire(text string, start int) (index int, size int) { - index = max(start, 0) - n := len(text) - for index < n { - next := strings.IndexAny(text[index:], "ir") - if next < 0 { - break - } - index += next - - var expected string - if text[index] == 'i' { - size = 6 - expected = "import" - } else { - size = 7 - expected = "require" - } - if index+size <= n && text[index:index+size] == expected { - return index, size +func findImportOrRequire(text string, start int, isJavaScriptFile bool) (index int, size int) { + start = max(start, 0) + if start >= len(text) { + return -1, 0 + } + sub := text[start:] + imp := strings.Index(sub, "import") + if !isJavaScriptFile { + if imp < 0 { + return -1, 0 } - index++ + return start + imp, 6 + } + req := strings.Index(sub, "require") + if imp >= 0 && (req < 0 || imp < req) { + return start + imp, 6 + } + if req >= 0 { + return start + req, 7 } - return -1, 0 } @@ -2848,7 +2905,7 @@ func ForEachDynamicImportOrRequireCall( cb func(node *Node, argument *Expression) bool, ) bool { isJavaScriptFile := IsInJSFile(file.AsNode()) - lastIndex, size := findImportOrRequire(file.Text(), 0) + lastIndex, size := findImportOrRequire(file.Text(), 0, isJavaScriptFile) for lastIndex >= 0 { node := GetNodeAtPosition(file, lastIndex, isJavaScriptFile && includeTypeSpaceImports) if isJavaScriptFile && IsRequireCall(node, requireStringLiteralLikeArgument) { @@ -2863,10 +2920,14 @@ func ForEachDynamicImportOrRequireCall( if cb(node, node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal) { return true } + } else if node.Kind == KindTypeAliasDeclaration && node.AsTypeAliasDeclaration().Type.IsLazyTypeNode() && node.End() > lastIndex+size { + lastIndex = node.End() + lastIndex, size = findImportOrRequire(file.Text(), lastIndex, isJavaScriptFile) + continue } // skip past import/require lastIndex += size - lastIndex, size = findImportOrRequire(file.Text(), lastIndex) + lastIndex, size = findImportOrRequire(file.Text(), lastIndex, isJavaScriptFile) } return false } diff --git a/tsc/internal/ast/visitor.go b/tsc/internal/ast/visitor.go index bedcc8b729eea..33694f0203764 100644 --- a/tsc/internal/ast/visitor.go +++ b/tsc/internal/ast/visitor.go @@ -96,7 +96,7 @@ func (v *NodeVisitor) VisitNodes(nodes *NodeList) *NodeList { return nodes } - if result, changed := v.VisitSlice(nodes.Nodes); changed { + if result, changed := v.VisitSlice(nodes.Slice()); changed { list := v.Factory.NewNodeList(result) list.Loc = nodes.Loc return list diff --git a/tsc/internal/binder/binder.go b/tsc/internal/binder/binder.go index a95bb19ecdff7..6864d8f229ec1 100644 --- a/tsc/internal/binder/binder.go +++ b/tsc/internal/binder/binder.go @@ -72,6 +72,7 @@ type Binder struct { inAssignmentPattern bool seenParseError bool symbolCount int + isLazy bool notConstEnumOnlyModules collections.Set[*ast.Symbol] symbolWithDataArena core.Arena[ast.SymbolWithData] symbolExtraArena ast.SymbolExtraArena @@ -100,6 +101,10 @@ func BindSourceFile(file *ast.SourceFile) { } } +func init() { + ast.SetLazyBindHook(bindLazyList) +} + var binderPool = sync.Pool{ New: func() any { b := &Binder{} @@ -108,12 +113,29 @@ var binderPool = sync.Pool{ }, } +func ResetWasmPools() { + binderPool = sync.Pool{ + New: func() any { + b := &Binder{} + b.bindFunc = b.bind + return b + }, + } +} + func getBinder() *Binder { return binderPool.Get().(*Binder) } func putBinder(b *Binder) { - *b = Binder{bindFunc: b.bindFunc} + *b = Binder{ + bindFunc: b.bindFunc, + symbolWithDataArena: b.symbolWithDataArena, + symbolExtraArena: b.symbolExtraArena, + flowNodeArena: b.flowNodeArena, + flowListArena: b.flowListArena, + singleDeclarationsArena: b.singleDeclarationsArena, + } binderPool.Put(b) } @@ -126,6 +148,9 @@ func bindSourceFile(file *ast.SourceFile) { b.bind(file.AsNode()) b.bindDeferredExpandoAssignments() file.SymbolCount = b.symbolCount + for _, lazy := range file.LazyLists { + lazy.MarkUnbound(file) + } }) } @@ -189,10 +214,10 @@ func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = symbolTable[name] + symbol = symbolTable.Get(name) if symbol == nil { symbol = b.newSymbol(ast.SymbolFlagsNone, name) - symbolTable[name] = symbol + symbolTable.Set(name, symbol) if isReplaceableByMethod { symbol.SetFlags(symbol.Flags() | ast.SymbolFlagsReplaceableByMethod) } @@ -204,7 +229,7 @@ func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. symbol = b.newSymbol(ast.SymbolFlagsNone, name) - symbolTable[name] = symbol + symbolTable.Set(name, symbol) } else if !(includes&ast.SymbolFlagsVariable != 0 && symbol.Flags()&ast.SymbolFlagsAssignment != 0 || includes&ast.SymbolFlagsAssignment != 0 && symbol.Flags()&ast.SymbolFlagsVariable != 0) { // Assignment declarations are allowed to merge with variables, no matter what other flags they have. @@ -550,14 +575,14 @@ func (b *Binder) addAntecedent(label *ast.FlowLabel, antecedent *ast.FlowNode) { } // If antecedent isn't already on the Antecedents list, add it to the end of the list var last *ast.FlowList - for list := label.Antecedents; list != nil; list = list.Next { + for list := label.Antecedents(); list != nil; list = list.Next { if list.Flow == antecedent { return } last = list } if last == nil { - label.Antecedents = b.newFlowList(antecedent, nil) + label.SetAntecedents(b.newFlowList(antecedent, nil)) } else { last.Next = b.newFlowList(antecedent, nil) } @@ -565,11 +590,12 @@ func (b *Binder) addAntecedent(label *ast.FlowLabel, antecedent *ast.FlowNode) { } func (b *Binder) finishFlowLabel(label *ast.FlowLabel) *ast.FlowNode { - if label.Antecedents == nil { + antecedents := label.Antecedents() + if antecedents == nil { return b.unreachableFlow } - if label.Antecedents.Next == nil { - return label.Antecedents.Flow + if antecedents.Next == nil { + return antecedents.Flow } return label } @@ -968,11 +994,11 @@ func (b *Binder) bindClassLikeDeclaration(node *ast.Node) { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. prototypeSymbol := b.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsPrototype, "prototype") - symbolExport := ast.GetExports(symbol, &b.symbolExtraArena)[prototypeSymbol.Name()] + symbolExport := ast.GetExports(symbol, &b.symbolExtraArena).Get(prototypeSymbol.Name()) if symbolExport != nil { b.errorOnNode(symbolExport.Declarations()[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol)) } - ast.GetExports(symbol, &b.symbolExtraArena)[prototypeSymbol.Name()] = prototypeSymbol + ast.GetExports(symbol, &b.symbolExtraArena).Set(prototypeSymbol.Name(), prototypeSymbol) prototypeSymbol.SetParent(symbol) } @@ -1001,16 +1027,16 @@ func (b *Binder) bindFunctionOrConstructorType(node *ast.Node) { b.addDeclarationToSymbol(symbol, node, ast.SymbolFlagsSignature) typeLiteralSymbol := b.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) b.addDeclarationToSymbol(typeLiteralSymbol, node, ast.SymbolFlagsTypeLiteral) - typeLiteralSymbol.SetMembers(make(ast.SymbolTable), &b.symbolExtraArena) - typeLiteralSymbol.Members()[symbol.Name()] = symbol + typeLiteralSymbol.SetMembers(ast.NewSymbolTable(), &b.symbolExtraArena) + typeLiteralSymbol.Members().Set(symbol.Name(), symbol) } func (b *Binder) addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbol) { exports := ast.GetExports(symbol, &b.symbolExtraArena) - assignmentSymbol := exports[ast.InternalSymbolNameAssignmentDeclaration] + assignmentSymbol := exports.Get(ast.InternalSymbolNameAssignmentDeclaration) if assignmentSymbol == nil { assignmentSymbol = b.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameAssignmentDeclaration) - exports[ast.InternalSymbolNameAssignmentDeclaration] = assignmentSymbol + exports.Set(ast.InternalSymbolNameAssignmentDeclaration, assignmentSymbol) } assignmentSymbol.SetDeclarations(append(assignmentSymbol.Declarations(), node)) } @@ -1045,10 +1071,10 @@ func (b *Binder) bindDeferredExpandoAssignments() { // symbol as a namespace module. func (b *Binder) bindCommonJSTypeExports(moduleSymbol *ast.Symbol) { moduleExports := moduleSymbol.Exports() - if exportEquals := moduleExports[ast.InternalSymbolNameExportEquals]; exportEquals != nil { - for _, symbol := range moduleExports { + if exportEquals := moduleExports.Get(ast.InternalSymbolNameExportEquals); exportEquals != nil { + for _, symbol := range moduleExports.All() { if symbol.Name() != ast.InternalSymbolNameExportEquals && symbol.Flags()&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { - ast.GetExports(exportEquals, &b.symbolExtraArena)[symbol.Name()] = symbol + ast.GetExports(exportEquals, &b.symbolExtraArena).Set(symbol.Name(), symbol) exportEquals.SetFlags(exportEquals.Flags() | ast.SymbolFlagsNamespaceModule) } } @@ -1068,7 +1094,7 @@ func (b *Binder) bindDeferredExpandoAssignment(node *ast.Node) { } else { // We declare expandos only when there are no non-expando declarations for that name. exports := ast.GetExports(symbol, &b.symbolExtraArena) - if existing := exports[b.getDeclarationName(node)]; existing == nil || existing.Flags()&ast.SymbolFlagsAssignment != 0 { + if existing := exports.Get(b.getDeclarationName(node)); existing == nil || existing.Flags()&ast.SymbolFlagsAssignment != 0 { b.declareSymbol(exports, symbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes) } } @@ -1278,14 +1304,14 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { if node.Expression().Kind == ast.KindThisKeyword { if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbolTable[name.Text()] + return symbolTable.Get(name.Text()) } } return nil } if symbol := getInitializerSymbol(b.lookupEntity(node.Expression(), container)); symbol != nil && symbol.Exports() != nil { if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbol.Exports()[name.Text()] + return symbol.Exports().Get(name.Text()) } } return nil @@ -1293,12 +1319,12 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol { if localsContainer := container.LocalsContainerData(); localsContainer != nil { - if local := localsContainer.Locals[name]; local != nil { + if local := localsContainer.Locals.Get(name); local != nil { return core.OrElse(local.ExportSymbol(), local) } } if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil { - return declaration.Symbol.Exports()[name] + return declaration.Symbol.Exports().Get(name) } return nil } @@ -1627,7 +1653,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { func (b *Binder) declareCommonJSVariable(name string) { locals := ast.GetLocals(b.file.AsNode()) - if locals[name] == nil { + if locals.Get(name) == nil { symbol := b.newSymbol(ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsModuleExports, name) symbol.SetDeclarations(b.newSingleDeclaration(b.file.AsNode())) symbol.SetValueDeclaration(symbol.Declarations()[0]) @@ -1636,10 +1662,10 @@ func (b *Binder) declareCommonJSVariable(name string) { exportsProperty.SetDeclarations(symbol.Declarations()) exportsProperty.SetValueDeclaration(symbol.ValueDeclaration()) exportsProperty.SetParent(symbol) - symbol.SetMembers(make(ast.SymbolTable, 1), &b.symbolExtraArena) - symbol.Members()["exports"] = exportsProperty + symbol.SetMembers(ast.NewSymbolTableWithCapacity(1), &b.symbolExtraArena) + symbol.Members().Set("exports", exportsProperty) } - locals[name] = symbol + locals.Set(name, symbol) } } @@ -1736,6 +1762,12 @@ func (b *Binder) bindChildren(node *ast.Node) { case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression, ast.KindPropertyAssignment, ast.KindSpreadElement: b.inAssignmentPattern = saveInAssignmentPattern b.bindEachChild(node) + case ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression, ast.KindTypeLiteral: + b.bindEachChild(node) + b.bindLazyMemberList(node) + case ast.KindTypeAliasDeclaration: + b.bindEachChild(node) + b.bindLazyTypeAlias(node) default: b.bindEachChild(node) } @@ -1752,6 +1784,97 @@ func (b *Binder) bindEach(nodes []*ast.Node) { } } +func (b *Binder) bindLazyMemberList(node *ast.Node) { + list := node.MemberList() + if !list.IsLazy() { + return + } + lazy := list.AsLazy() + if lazy.State() != ast.LazyListPending { + return + } + symbol := node.Symbol() + plain := symbol != nil && + b.currentFlow != b.unreachableFlow && + b.currentBreakTarget == nil && + b.currentContinueTarget == nil && + b.currentReturnTarget == nil && + b.currentTrueTarget == nil && + b.currentFalseTarget == nil && + b.currentExceptionTarget == nil && + b.preSwitchCaseFlow == nil && + b.activeLabelList == nil && + !b.inAssignmentPattern + if !plain { + nodes := lazy.ParseNow(b.file) + b.bindEach(nodes) + for _, nested := range lazy.NestedLazyLists() { + nested.MarkUnbound(b.file) + } + lazy.SetNestedLazyLists(nil) + return + } + lazy.Defer(b.file, &ast.LazyBindContext{ + Container: b.container, + ThisContainer: b.thisContainer, + BlockScopeContainer: b.blockScopeContainer, + CurrentFlow: b.currentFlow, + UnreachableFlow: b.unreachableFlow, + }) + symbol.SetHasLazyMembers() +} + +func (b *Binder) bindLazyTypeAlias(node *ast.Node) { + typeNode := node.AsTypeAliasDeclaration().Type + if !typeNode.IsLazyTypeNode() { + return + } + lazy := typeNode.AsLazyTypeNode() + plain := b.currentFlow != b.unreachableFlow && + b.currentBreakTarget == nil && + b.currentContinueTarget == nil && + b.currentReturnTarget == nil && + b.currentTrueTarget == nil && + b.currentFalseTarget == nil && + b.currentExceptionTarget == nil && + b.preSwitchCaseFlow == nil && + b.activeLabelList == nil && + !b.inAssignmentPattern + if !plain { + nodes := lazy.ParseNow(b.file) + b.bindEach(nodes) + for _, nested := range lazy.NestedLazyLists() { + nested.MarkUnbound(b.file) + } + lazy.SetNestedLazyLists(nil) + return + } + lazy.Defer(b.file, &ast.LazyBindContext{ + Container: b.container, + ThisContainer: b.thisContainer, + BlockScopeContainer: b.blockScopeContainer, + CurrentFlow: b.currentFlow, + UnreachableFlow: b.unreachableFlow, + }) +} + +func bindLazyList(lazy *ast.LazyNodeList, file *ast.SourceFile, nodes []*ast.Node) { + ctx := lazy.BindCtx + if ctx == nil { + return + } + b := getBinder() + defer putBinder(b) + b.file = file + b.isLazy = true + b.container = ctx.Container + b.thisContainer = ctx.ThisContainer + b.blockScopeContainer = ctx.BlockScopeContainer + b.currentFlow = ctx.CurrentFlow + b.unreachableFlow = ctx.UnreachableFlow + b.bindEach(nodes) +} + func (b *Binder) bindNodeList(nodeList *ast.NodeList) { if nodeList != nil { b.bindEach(nodeList.Nodes) @@ -2062,7 +2185,7 @@ func (b *Binder) bindTryStatement(node *ast.Node) { // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel // node, the pre-finally label is temporarily switched to the reduced antecedent set. finallyLabel := b.createBranchLabel() - finallyLabel.Antecedents = b.combineFlowLists(normalExitLabel.Antecedents, b.combineFlowLists(exceptionLabel.Antecedents, returnLabel.Antecedents)) + finallyLabel.SetAntecedents(b.combineFlowLists(normalExitLabel.Antecedents(), b.combineFlowLists(exceptionLabel.Antecedents(), returnLabel.Antecedents()))) b.currentFlow = finallyLabel b.bind(stmt.FinallyBlock) if b.currentFlow.Flags&ast.FlowFlagsUnreachable != 0 { @@ -2071,19 +2194,19 @@ func (b *Binder) bindTryStatement(node *ast.Node) { } else { // If we have an IIFE return target and return statements in the try or catch blocks, add a control // flow that goes back through the finally block and back through only the return statements. - if b.currentReturnTarget != nil && returnLabel.Antecedents != nil { - b.addAntecedent(b.currentReturnTarget, b.createReduceLabel(finallyLabel, returnLabel.Antecedents, b.currentFlow)) + if b.currentReturnTarget != nil && returnLabel.Antecedents() != nil { + b.addAntecedent(b.currentReturnTarget, b.createReduceLabel(finallyLabel, returnLabel.Antecedents(), b.currentFlow)) } // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a // control flow that goes back through the finally block and back through each possible exception source. - if b.currentExceptionTarget != nil && exceptionLabel.Antecedents != nil { - b.addAntecedent(b.currentExceptionTarget, b.createReduceLabel(finallyLabel, exceptionLabel.Antecedents, b.currentFlow)) + if b.currentExceptionTarget != nil && exceptionLabel.Antecedents() != nil { + b.addAntecedent(b.currentExceptionTarget, b.createReduceLabel(finallyLabel, exceptionLabel.Antecedents(), b.currentFlow)) } // If the end of the finally block is reachable, but the end of the try and catch blocks are not, // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should // result in an unreachable current control flow. - if normalExitLabel.Antecedents != nil { - b.currentFlow = b.createReduceLabel(finallyLabel, normalExitLabel.Antecedents, b.currentFlow) + if normalExitLabel.Antecedents() != nil { + b.currentFlow = b.createReduceLabel(finallyLabel, normalExitLabel.Antecedents(), b.currentFlow) } else { b.currentFlow = b.unreachableFlow } @@ -2543,7 +2666,7 @@ func (b *Binder) addDeclarationToSymbol(symbol *ast.Symbol, node *ast.Node, symb symbol.SetFlags(symbol.Flags() | symbolFlags) node.DeclarationData().Symbol = symbol if symbol.Declarations() == nil { - symbol.SetDeclarations(b.newSingleDeclaration(node)) + symbol.SetSingleDeclaration(node) } else { symbol.SetDeclarations(core.AppendIfUnique(symbol.Declarations(), node)) } @@ -2742,7 +2865,9 @@ func (b *Binder) createDiagnosticForNode(node *ast.Node, message *diagnostics.Me } func (b *Binder) addDiagnostic(diagnostic *ast.Diagnostic) { - b.file.SetBindDiagnostics(append(b.file.BindDiagnostics(), diagnostic)) + if !b.isLazy { + b.file.SetBindDiagnostics(append(b.file.BindDiagnostics(), diagnostic)) + } } func isSignedNumericLiteral(node *ast.Node) bool { diff --git a/tsc/internal/binder/nameresolver.go b/tsc/internal/binder/nameresolver.go index a7eff7005e812..93272224cf81a 100644 --- a/tsc/internal/binder/nameresolver.go +++ b/tsc/internal/binder/nameresolver.go @@ -14,6 +14,7 @@ type NameResolver struct { ArgumentsSymbol *ast.Symbol RequireSymbol *ast.Symbol Lookup func(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol + LookupSymbol func(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol SymbolReferenced func(symbol *ast.Symbol, meaning ast.SymbolFlags) SetRequiresScopeChangeCache func(node *ast.Node, value core.Tristate) GetRequiresScopeChangeCache func(node *ast.Node) core.Tristate @@ -22,6 +23,53 @@ type NameResolver struct { OnSuccessfullyResolvedSymbol func(location *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool) } +var isActiveScopeKind = [ast.KindCount + 4]bool{ + ast.KindForStatement: true, + ast.KindForInStatement: true, + ast.KindForOfStatement: true, + ast.KindCaseBlock: true, + ast.KindCatchClause: true, + ast.KindBlock: true, + ast.KindFunctionDeclaration: true, + ast.KindClassDeclaration: true, + ast.KindClassExpression: true, + ast.KindTypeAliasDeclaration: true, + ast.KindJSTypeAliasDeclaration: true, + ast.KindCallSignature: true, + ast.KindConstructSignature: true, + ast.KindConstructor: true, + ast.KindGetAccessor: true, + ast.KindSetAccessor: true, + ast.KindIndexSignature: true, + ast.KindMethodSignature: true, + ast.KindMethodDeclaration: true, + ast.KindClassStaticBlockDeclaration: true, + ast.KindArrowFunction: true, + ast.KindFunctionExpression: true, + ast.KindConditionalType: true, + ast.KindMappedType: true, + ast.KindFunctionType: true, + ast.KindConstructorType: true, + ast.KindJSDocSignature: true, + ast.KindSourceFile: true, + ast.KindModuleDeclaration: true, + ast.KindImportAttributes: true, + ast.KindEnumDeclaration: true, + ast.KindPropertyDeclaration: true, + ast.KindInterfaceDeclaration: true, + ast.KindExpressionWithTypeArguments: true, + ast.KindComputedPropertyName: true, + ast.KindDecorator: true, + ast.KindParameter: true, + ast.KindBindingElement: true, + ast.KindInferType: true, + ast.KindExportSpecifier: true, + ast.KindTypeQuery: true, + ast.KindJSDocParameterTag: true, + ast.KindJSDocReturnTag: true, + ast.KindJSDocTemplateTag: true, +} + func (r *NameResolver) Resolve(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol { var result *ast.Symbol var lastLocation *ast.Node @@ -34,10 +82,34 @@ func (r *NameResolver) Resolve(location *ast.Node, name string, meaning ast.Symb nameIsConst := name == "const" loop: for location != nil { - if nameIsConst && ast.IsConstAssertion(location) { - // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type - // (it refers to the constant type of the expression instead) - return nil + if !nameIsConst { + for { + if location.Kind == ast.KindBlock { + if locals := location.AsBlock().Locals; locals.Len() != 0 { + if result = r.lookup(locals, name, meaning); result != nil { + break loop + } + } + } else if isActiveScopeKind[location.Kind] { + break + } + lastLocation = location + location = location.Parent + if location == nil { + break loop + } + } + } else { + if ast.IsConstAssertion(location) { + // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type + // (it refers to the constant type of the expression instead) + return nil + } + if !isActiveScopeKind[location.Kind] { + lastLocation = location + location = location.Parent + continue + } } if ast.IsModuleOrEnumDeclaration(location) && lastLocation != nil && location.Name() == lastLocation { // If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could @@ -50,7 +122,7 @@ loop: lastLocation == location.AsModuleDeclaration().Attributes locals := location.Locals() // Locals of a source file are not in scope (because they get merged into the global symbol table) - if locals != nil && !ast.IsGlobalSourceFile(location) { + if locals.Len() != 0 && !ast.IsGlobalSourceFile(location) { result = r.lookup(locals, name, meaning) if result != nil { useResult := true @@ -99,7 +171,7 @@ loop: result = nil } } - withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation) + withinDeferredContext = withinDeferredContext || (nameNotFoundMessage != nil && meaning&ast.SymbolFlagsValue == ast.SymbolFlagsValue && getIsDeferredContext(location, lastLocation)) switch location.Kind { case ast.KindSourceFile: if !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { @@ -115,10 +187,13 @@ loop: break } moduleExports := moduleSymbol.Exports() + if moduleExports.Len() == 0 { + break + } if ast.IsSourceFile(location) || (ast.IsModuleDeclaration(location) && location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsGlobalScopeAugmentation(location)) { // It's an external module. First see if the module has an export default and if the local // name of that export default matches. - result = moduleExports[ast.InternalSymbolNameDefault] + result = moduleExports.Get(ast.InternalSymbolNameDefault) if result != nil { localSymbol := GetLocalSymbolForExportDefault(result) if localSymbol != nil && result.Flags()&meaning != 0 && localSymbol.Name() == name { @@ -137,10 +212,30 @@ loop: // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. - moduleExport := moduleExports[name] + moduleExport := moduleExports.Get(name) if moduleExport != nil && moduleExport.Flags() == ast.SymbolFlagsAlias && (ast.GetDeclarationOfKind(moduleExport, ast.KindExportSpecifier) != nil || ast.GetDeclarationOfKind(moduleExport, ast.KindNamespaceExport) != nil) { break } + if r.Lookup == nil { + if moduleExport == nil { + break + } + if name != ast.InternalSymbolNameDefault { + if r.LookupSymbol != nil { + result = r.LookupSymbol(moduleExport, meaning&ast.SymbolFlagsModuleMember) + } else if moduleExport.Flags()&(meaning&ast.SymbolFlagsModuleMember) != 0 { + result = moduleExport + } + if result != nil { + if ast.IsSourceFile(location) && location.AsSourceFile().CommonJSModuleIndicator != nil && result.Flags()&ast.SymbolFlagsType == 0 { + result = nil + } else { + break loop + } + } + } + break + } } if name != ast.InternalSymbolNameDefault { if result = r.lookup(moduleExports, name, meaning&ast.SymbolFlagsModuleMember); result != nil { @@ -176,23 +271,25 @@ loop: } } case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: - result = r.lookup(r.getSymbolOfDeclaration(location).Members(), name, meaning&ast.SymbolFlagsType) - if result != nil { - if !isTypeParameterSymbolDeclaredInContainer(result, location) { - // ignore type parameters not declared in this container - result = nil - break - } - if lastLocation != nil && ast.IsStatic(lastLocation) { - // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration with which the type - // parameter list is associated, with the exception of static member declarations in classes. - if nameNotFoundMessage != nil { - r.error(originalLocation, diagnostics.Static_members_cannot_reference_class_type_parameters) + if meaning&ast.SymbolFlagsTypeParameter != 0 && (len(location.TypeParameters()) != 0 || ast.IsInJSFile(location)) { + result = r.lookup(r.getSymbolOfDeclaration(location).Members(), name, meaning&ast.SymbolFlagsType) + if result != nil { + if !isTypeParameterSymbolDeclaredInContainer(result, location) { + // ignore type parameters not declared in this container + result = nil + break } - return nil + if lastLocation != nil && ast.IsStatic(lastLocation) { + // TypeScript 1.0 spec (April 2014): 3.4.1 + // The scope of a type parameter extends over the entire declaration with which the type + // parameter list is associated, with the exception of static member declarations in classes. + if nameNotFoundMessage != nil { + r.error(originalLocation, diagnostics.Static_members_cannot_reference_class_type_parameters) + } + return nil + } + break loop } - break loop } if ast.IsClassExpression(location) && meaning&ast.SymbolFlagsClass != 0 { className := location.Name() @@ -204,7 +301,7 @@ loop: case ast.KindExpressionWithTypeArguments: if lastLocation == location.Expression() && ast.IsHeritageClause(location.Parent) && location.Parent.AsHeritageClause().Token == ast.KindExtendsKeyword { container := location.Parent.Parent - if ast.IsClassLike(container) { + if ast.IsClassLike(container) && meaning&ast.SymbolFlagsTypeParameter != 0 && (len(container.TypeParameters()) != 0 || ast.IsInJSFile(container)) { result = r.lookup(r.getSymbolOfDeclaration(container).Members(), name, meaning&ast.SymbolFlagsType) if result != nil { if nameNotFoundMessage != nil { @@ -223,8 +320,7 @@ loop: // } case ast.KindComputedPropertyName: grandparent = location.Parent.Parent - if ast.IsClassLike(grandparent) || ast.IsInterfaceDeclaration(grandparent) { - // A reference to this grandparent's type parameters would be an error + if (ast.IsClassLike(grandparent) || ast.IsInterfaceDeclaration(grandparent)) && meaning&ast.SymbolFlagsTypeParameter != 0 && (len(grandparent.TypeParameters()) != 0 || ast.IsInJSFile(grandparent)) { result = r.lookup(r.getSymbolOfDeclaration(grandparent).Members(), name, meaning&ast.SymbolFlagsType) if result != nil { if nameNotFoundMessage != nil { @@ -424,12 +520,21 @@ func (r *NameResolver) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { } func (r *NameResolver) lookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { + if meaning == 0 || symbols.Len() == 0 { + return nil + } + if r.LookupSymbol != nil { + if symbol := symbols.Get(name); symbol != nil { + return r.LookupSymbol(symbol, meaning) + } + return nil + } if r.Lookup != nil { return r.Lookup(symbols, name, meaning) } // Default implementation does not support following aliases or merged symbols if meaning != 0 { - symbol := symbols[name] + symbol := symbols.Get(name) if symbol != nil { if symbol.Flags()&meaning != 0 { return symbol diff --git a/tsc/internal/bundled/bundled.go b/tsc/internal/bundled/bundled.go index 3007f55ad2e1f..a6fcf3e301f68 100644 --- a/tsc/internal/bundled/bundled.go +++ b/tsc/internal/bundled/bundled.go @@ -5,7 +5,6 @@ import ( "path/filepath" "runtime" "sync" - "testing" "github.com/microsoft/TypeScript/tsc/internal/tspath" "github.com/microsoft/TypeScript/tsc/internal/vfs" @@ -44,9 +43,6 @@ var bundledSourceDir = sync.OnceValue(func() tspath.RootedDirectoryPath { }) var testingLibPath = sync.OnceValue(func() string { - if !testing.Testing() { - panic("bundled: TestingLibPath should only be called during tests") - } return bundledSourceDir().ResolveDirectory("libs").AsString() }) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 148cf5fde47dd..e66ba475e4f0a 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -7,6 +7,8 @@ import ( "iter" "maps" "math" + "math/bits" + "runtime" "slices" "strconv" "strings" @@ -71,6 +73,7 @@ type TypeResolution struct { target TypeSystemEntity propertyName TypeSystemPropertyName result bool + serial uint32 } // ContextualInfo @@ -258,8 +261,9 @@ type FlowLoopKey struct { } type FlowLoopInfo struct { - key FlowLoopKey - types []*Type + key FlowLoopKey + types []*Type + serial uint32 } // InferenceFlags @@ -288,6 +292,7 @@ type InferenceContext struct { inferredTypeParameters []*Type // Inferred type parameters for function result intraExpressionInferenceSites []IntraExpressionInferenceSite mapperStorage [2]InferenceTypeMapper + escaped bool } type InferenceInfo struct { @@ -299,6 +304,7 @@ type InferenceInfo struct { topLevel bool // True if all inferences are to top level occurrences isFixed bool // True if inferences are fixed impliedArity int // Implied arity (or -1) + candidateStorage [2]*Type } type InferencePriority int32 @@ -631,8 +637,8 @@ type Checker struct { bigintLiteralTypes map[jsnum.PseudoBigInt]*Type enumLiteralTypes map[EnumLiteralKey]*Type enumNaNLiteralTypes map[*ast.Symbol]*Type - indexedAccessTypes map[CacheHashKey]*Type - templateLiteralTypes map[CacheHashKey]*Type + indexedAccessTypes typeCacheTable + templateLiteralTypes typeCacheTable stringMappingTypes map[StringMappingKey]*Type uniqueESSymbolTypes map[*ast.Symbol]*Type thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind @@ -658,17 +664,18 @@ type Checker struct { requireSymbol *ast.Symbol unknownSymbol *ast.Symbol unresolvedSymbols map[string]*ast.Symbol - errorTypes map[CacheHashKey]*Type + errorTypes typeCacheTable moduleSymbols map[*ast.Node]*ast.Symbol globalThisSymbol *ast.Symbol symbolTableAliasCache map[symbolTableID][]*ast.Symbol classExpressionNameTables map[ast.NodeId]ast.SymbolTable resolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol resolveNameForSymbolSuggestion func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol - tupleTypes map[CacheHashKey]*Type - unionTypes map[CacheHashKey]*Type + tupleTypes typeCacheTable + unionTypes typeCacheTable unionOfUnionTypes map[UnionOfUnionKey]*Type - intersectionTypes map[CacheHashKey]*Type + unionDiscriminants map[*UnionType]unionDiscriminant + intersectionTypes typeCacheTable propertiesTypes map[PropertiesTypesKey]*Type diagnostics ast.DiagnosticsCollection suggestionDiagnostics ast.DiagnosticsCollection @@ -688,21 +695,67 @@ type Checker struct { typeParameterArena core.Arena[TypeParameter] indexedAccessTypeArena core.Arena[IndexedAccessType] conditionalTypeArena core.Arena[ConditionalType] + conditionalTypeExtraArena core.Arena[conditionalTypeExtra] typeListArena core.Arena[*Type] + signatureListArena core.Arena[*Signature] + indexInfoListArena core.Arena[*IndexInfo] outerTypeParameterListArena core.Arena[[]*Type] valueSymbolExtraArena core.Arena[valueSymbolExtra] structuredTypeExtraArena core.Arena[structuredTypeExtra] + typeCacheTableArena core.Arena[typeCacheTable] + typeCacheSlotsArena core.Arena[typeCacheSlot] + freeTypeCacheSlots [20][][]typeCacheSlot + memberIndexArena core.Arena[memberIndex] + memberIndexSlotsArena core.Arena[uint32] lazyStateArena core.Arena[LazyState] memberLayoutArena core.Arena[MemberLayout] + memberInfoListArena core.Arena[MemberInfo] + symbolListArena core.Arena[*ast.Symbol] layoutMemberInfos map[string]MemberInfo + lazyMappedTableArena core.Arena[lazyMappedTable] + simpleMapperArena core.Arena[SimpleTypeMapper] + arrayMapperArena core.Arena[ArrayTypeMapper] + mergedMapperArena core.Arena[MergedTypeMapper] + compositeMapperArena core.Arena[CompositeTypeMapper] + typeAliasArena core.Arena[TypeAlias] + freeTypeArgLists [][]*Type + freeSymbolLists [][]*ast.Symbol + unionFrontCache map[CacheHashKey]*Type + unionFrontSlots *[1024]unionFrontSlot + unionImpureCount uint32 + diagnosticCount uint32 + instantiationCountResets uint32 + resolutionCycleCount uint32 + inferMemoSteps uint32 + inferLongTargets map[*Type]bool + inferMemoEntries map[CacheHashKey]*inferMemoEntry + flowMemo flowMemo + indexedAccessUnionMemo map[indexedAccessUnionKey]*Type + globalObjectAugmentFilter [4]uint64 + unionConstituentFlags map[*Type]TypeFlags + globalObjectFilterReady bool freeTypeSets *temporaryTypeSet freePropertyCounts *temporaryPropertyCounts + freeNameSets []map[string]struct{} + freeInferenceContexts []*InferenceContext + freeInferenceInfos []*InferenceInfo + templateScratchPool []*templateLiteralScratch + leafKeyBuilder keyBuilder + namedUnionMemberSet map[*Type]struct{} + unionRunStarts []int + unionRunBounds []int + unionRunNext []int + unionMergeBuf []*Type + symbolSortBuf []symbolSortEntry + nodeFileIndexCache *[4096]nodeFileIndexEntry mergedSymbols map[*ast.Symbol]*ast.Symbol mergedExportsChecked collections.Set[*ast.Symbol] factory ast.NodeFactory + nodeIdGen ast.NodeIdGenerator + symbolIdGen ast.SymbolIdGenerator nodeLinks core.LinkStore[*ast.Node, NodeLinks] signatureLinks nodeLinkStore[SignatureLinks] - symbolNodeLinks nodeLinkStore[SymbolNodeLinks] + symbolNodeLinks directNodeLinkStore[SymbolNodeLinks] typeNodeLinks nodeLinkStore[TypeNodeLinks] enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] assertionLinks core.LinkStore[*ast.Node, AssertionLinks] @@ -710,9 +763,7 @@ type Checker struct { switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] computedNameLinks core.LinkStore[*ast.Node, ComputedNameNodeLinks] - symbolReferenceLinks core.LinkStore[*ast.Symbol, SymbolReferenceLinks] valueSymbolLinks symbolLinkStore[ValueSymbolLinks] - mappedSymbolLinks core.LinkStore[*ast.Symbol, MappedSymbolLinks] deferredSymbolLinks core.LinkStore[*ast.Symbol, DeferredSymbolLinks] aliasSymbolLinks core.LinkStore[*ast.Symbol, AliasSymbolLinks] moduleSymbolLinks core.LinkStore[*ast.Symbol, ModuleSymbolLinks] @@ -842,8 +893,8 @@ type Checker struct { flowTypeCache map[*ast.Node]*Type lastFlowNode *ast.FlowNode lastFlowNodeReachable bool - flowNodeReachable map[*ast.FlowNode]bool - flowNodePostSuper map[*ast.FlowNode]bool + flowReachableGen uint32 + flowReachableSlots [2048]flowReachableSlot renamedBindingElementsInTypes []*ast.Node contextualInfos []ContextualInfo inferenceContextInfos []InferenceContextInfo @@ -913,8 +964,6 @@ type Checker struct { syncIterationTypesResolver *IterationTypesResolver asyncIterationTypesResolver *IterationTypesResolver isPrimitiveOrObjectOrEmptyType func(*Type) bool - containsMissingType func(*Type) bool - couldContainTypeVariables func(*Type) bool isStringIndexSignatureOnlyType func(*Type) bool markNodeAssignments func(*ast.Node) bool compareTypesAssignable TypeComparer @@ -925,7 +974,10 @@ type Checker struct { ctx context.Context packagesMap map[string]bool activeMappers []*TypeMapper - activeTypeMappersCaches []map[CacheHashKey]*Type + activeTypeMappersCaches []activeMapperCache + freeObjectLiteralSeen []map[string]DeclarationMeaning + freeSymbolTables []ast.SymbolTable + freeCallStates []*CallState ambientModulesOnce sync.Once ambientModules []*ast.Symbol withinUnreachableCode bool @@ -941,7 +993,7 @@ type Checker struct { func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { program.BindSourceFiles() - c := &Checker{} + c := &Checker{flowMemo: newFlowMemo()} c.id = nextCheckerID.Add(1) c.tracer = tracer c.program = program @@ -966,15 +1018,13 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.exactOptionalPropertyTypes = c.compilerOptions.ExactOptionalPropertyTypes == core.TSTrue c.canCollectSymbolAliasAccessibilityData = c.compilerOptions.VerbatimModuleSyntax.IsFalseOrUnknown() c.arrayVariances = []VarianceFlags{VarianceFlagsCovariant} - c.globals = make(ast.SymbolTable, countGlobalSymbols(c.files)) + c.globals = ast.NewSymbolTableWithCapacity(countGlobalSymbols(c.files)) c.evaluate = evaluator.NewEvaluator(c.evaluateEntity, ast.OEKParentheses) c.stringLiteralTypes = make(map[string]*Type) c.numberLiteralTypes = make(map[jsnum.Number]*Type) c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type) c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) c.enumNaNLiteralTypes = make(map[*ast.Symbol]*Type) - c.indexedAccessTypes = make(map[CacheHashKey]*Type) - c.templateLiteralTypes = make(map[CacheHashKey]*Type) c.stringMappingTypes = make(map[StringMappingKey]*Type) c.uniqueESSymbolTypes = make(map[*ast.Symbol]*Type) c.thisExpandoKinds = make(map[*ast.Symbol]thisAssignmentDeclarationKind) @@ -996,19 +1046,19 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.requireSymbol = c.newSymbol(ast.SymbolFlagsProperty, "require") c.unknownSymbol = c.newSymbol(ast.SymbolFlagsProperty, "unknown") c.unresolvedSymbols = make(map[string]*ast.Symbol) - c.errorTypes = make(map[CacheHashKey]*Type) c.moduleSymbols = make(map[*ast.Node]*ast.Symbol) c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.globalThisSymbol.SetExports(c.globals, &c.symbolExtraArena) - c.globals[c.globalThisSymbol.Name()] = c.globalThisSymbol + c.globals.Set(c.globalThisSymbol.Name(), c.globalThisSymbol) c.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve - c.tupleTypes = make(map[CacheHashKey]*Type) - c.unionTypes = make(map[CacheHashKey]*Type) c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) - c.intersectionTypes = make(map[CacheHashKey]*Type) c.propertiesTypes = make(map[PropertiesTypesKey]*Type) c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol) + c.signatureLinks.gen = &c.nodeIdGen + c.symbolNodeLinks.gen = &c.nodeIdGen + c.typeNodeLinks.gen = &c.nodeIdGen + c.valueSymbolLinks.gen = &c.symbolIdGen c.patternForType = make(map[*Type]*ast.Node) c.contextFreeTypes = make(map[*ast.Node]*Type) c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") @@ -1064,7 +1114,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.unknownEmptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.unknownUnionType = c.createUnknownUnionType() c.emptyGenericType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyGenericType.AsObjectType().instantiations = make(map[CacheHashKey]*Type) + c.emptyGenericType.AsObjectType() c.anyFunctionType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.anyFunctionType.objectFlags |= ObjectFlagsNonInferrableType c.noConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) @@ -1090,13 +1140,8 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{}) c.typeofType = c.getUnionType(core.Map(slices.Sorted(maps.Keys(typeofNEFacts)), c.getStringLiteralType)) c.flowLoopCache = make(map[FlowLoopKey]*Type) - c.flowNodeReachable = make(map[*ast.FlowNode]bool) - c.flowNodePostSuper = make(map[*ast.FlowNode]bool) - c.subtypeRelation = &Relation{} - c.strictSubtypeRelation = &Relation{} - c.assignableRelation = &Relation{} - c.comparableRelation = &Relation{} - c.identityRelation = &Relation{} + c.flowReachableGen = 2 + c.subtypeRelation, c.strictSubtypeRelation, c.assignableRelation, c.comparableRelation, c.identityRelation = newRelations() c.enumRelation = make(map[EnumRelationKey]RelationComparisonResult) c.moduleImportAttributesTypes = make(map[*ast.Symbol]*Type) c.getGlobalESSymbolType = c.getGlobalTypeResolver("Symbol", 0 /*arity*/, false /*reportErrors*/) @@ -1168,7 +1213,7 @@ func countGlobalSymbols(files []*ast.SourceFile) int { count := 0 for _, file := range files { if !ast.IsExternalOrCommonJSModule(file) { - count += len(file.Locals) + count += file.Locals.Len() } } return count @@ -1288,15 +1333,15 @@ func (c *Checker) initializeClosures() { c.isPrimitiveOrObjectOrEmptyType = func(t *Type) bool { return t.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 || c.IsEmptyAnonymousObjectType(t) } - c.containsMissingType = func(t *Type) bool { - return t == c.missingType || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == c.missingType - } - c.couldContainTypeVariables = c.couldContainTypeVariablesWorker c.isStringIndexSignatureOnlyType = c.isStringIndexSignatureOnlyTypeWorker c.markNodeAssignments = c.markNodeAssignmentsWorker c.compareTypesAssignable = c.compareTypesAssignableWorker } +func (c *Checker) containsMissingType(t *Type) bool { + return t == c.missingType || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == c.missingType +} + func (c *Checker) initializeIterationResolvers() { c.syncIterationTypesResolver = &IterationTypesResolver{ iteratorSymbolName: "iterator", @@ -1341,12 +1386,12 @@ func (c *Checker) initializeChecker() { for _, file := range c.files { if !ast.IsExternalOrCommonJSModule(file) { // It is an error for a non-external-module (i.e. script) to declare its own `globalThis`. - if fileGlobalThisSymbol := file.Locals["globalThis"]; fileGlobalThisSymbol != nil { + if fileGlobalThisSymbol := file.Locals.Get("globalThis"); fileGlobalThisSymbol != nil { for _, d := range fileGlobalThisSymbol.Declarations() { c.addDiagnostic(NewDiagnosticForNode(d, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, "globalThis")) } } - for _, symbol := range file.Locals { + for _, symbol := range file.Locals.All() { // We defer merging of global ambient module declarations since they may require other global symbols // and types to be resolved. See https://github.com/microsoft/TypeScript/tsc/issues/2953. if symbol.Flags()&ast.SymbolFlagsModule != 0 && ast.IsAmbientModuleSymbolName(symbol.Name()) { @@ -1360,9 +1405,9 @@ func (c *Checker) initializeChecker() { augmentations = append(augmentations, file.ModuleAugmentations) if file.Symbol != nil { // Merge in UMD exports with first-in-wins semantics (see #9771) - for name, symbol := range file.GlobalExports { - if _, ok := c.globals[name]; !ok { - c.globals[name] = symbol + for name, symbol := range file.GlobalExports.All() { + if _, ok := c.globals.Lookup(name); !ok { + c.globals.Set(name, symbol) } } } @@ -1426,14 +1471,14 @@ func (c *Checker) initializeChecker() { } func (c *Checker) mergeGlobalSymbol(symbol *ast.Symbol) { - globalSymbol := c.globals[symbol.Name()] + globalSymbol := c.globals.Get(symbol.Name()) var merged *ast.Symbol if globalSymbol != nil { merged = c.mergeSymbol(globalSymbol, symbol, false /*unidirectional*/) } else { merged = c.getMergedSymbol(symbol) } - c.globals[symbol.Name()] = merged + c.globals.Set(symbol.Name(), merged) } // Pattern ambient modules are merged together if they have the same pattern and identical import attributes type. @@ -1457,8 +1502,8 @@ func (c *Checker) mergePatternAmbientModules() { } } for _, module := range c.patternAmbientModules { - if _, ok := c.globals[module.Symbol.Name()]; ok { - c.globals[module.Symbol.Name()] = c.getMergedSymbol(module.Symbol) + if _, ok := c.globals.Lookup(module.Symbol.Name()); ok { + c.globals.Set(module.Symbol.Name(), c.getMergedSymbol(module.Symbol)) } } c.patternAmbientModules = grouped @@ -1500,15 +1545,15 @@ func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { }) { merged := c.mergeSymbol(moduleAugmentation.Symbol, mainModule, true /*unidirectional*/) // moduleName will be a StringLiteral since this is not `declare global`. - ast.GetSymbolTable(&c.patternAmbientModuleAugmentations)[moduleName.Text()] = merged - ast.GetSymbolTable(&c.patternAmbientModuleAugmentationTargets)[moduleName.Text()] = mainModule + ast.GetSymbolTable(&c.patternAmbientModuleAugmentations).Set(moduleName.Text(), merged) + ast.GetSymbolTable(&c.patternAmbientModuleAugmentationTargets).Set(moduleName.Text(), mainModule) } else { - if mainModule.Exports()[ast.InternalSymbolNameExportStar] != nil && len(moduleAugmentation.Symbol.Exports()) != 0 { + if mainModule.Exports().Get(ast.InternalSymbolNameExportStar) != nil && moduleAugmentation.Symbol.Exports().Len() != 0 { // We may need to merge the module augmentation's exports into the target symbols of the resolved exports resolvedExports := c.getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKindResolvedExports) - for key, value := range moduleAugmentation.Symbol.Exports() { - if resolvedExports[key] != nil && mainModule.Exports()[key] == nil { - c.mergeSymbol(resolvedExports[key], value, false /*unidirectional*/) + for key, value := range moduleAugmentation.Symbol.Exports().All() { + if resolvedExports.Get(key) != nil && mainModule.Exports().Get(key) == nil { + c.mergeSymbol(resolvedExports.Get(key), value, false /*unidirectional*/) } } } @@ -1523,7 +1568,7 @@ func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { name := c.undefinedSymbol.Name() - targetSymbol := c.globals[name] + targetSymbol := c.globals.Get(name) if targetSymbol != nil { for _, declaration := range targetSymbol.Declarations() { if !ast.IsTypeDeclaration(declaration) { @@ -1531,7 +1576,7 @@ func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { } } } else { - c.globals[name] = c.undefinedSymbol + c.globals.Set(name, c.undefinedSymbol) } } @@ -1543,7 +1588,7 @@ func (c *Checker) createNameResolver() *binder.NameResolver { Globals: c.globals, ArgumentsSymbol: c.argumentsSymbol, RequireSymbol: c.requireSymbol, - Lookup: c.getSymbol, + LookupSymbol: c.resolveSymbolLookup, SymbolReferenced: c.symbolReferenced, SetRequiresScopeChangeCache: c.setRequiresScopeChangeCache, GetRequiresScopeChangeCache: c.getRequiresScopeChangeCache, @@ -1569,7 +1614,36 @@ func (c *Checker) createNameResolverForSuggestion() *binder.NameResolver { } func (c *Checker) symbolReferenced(symbol *ast.Symbol, meaning ast.SymbolFlags) { - c.symbolReferenceLinks.Get(symbol).referenceKinds |= meaning + symbol.AddReference(meaning) +} + +func (c *Checker) acquireSymbolTable() ast.SymbolTable { + if n := len(c.freeSymbolTables); n > 0 { + m := c.freeSymbolTables[n-1] + c.freeSymbolTables = c.freeSymbolTables[:n-1] + return m + } + return ast.NewSymbolTable() +} + +func (c *Checker) releaseSymbolTable(m ast.SymbolTable) { + if m != nil { + if m.Len() > 4096 { + return + } + m.Clear() + c.freeSymbolTables = append(c.freeSymbolTables, m) + } +} + +func (c *Checker) releaseSymbolTableIfUnused(t *Type, m ast.SymbolTable) { + if m == nil { + return + } + data := t.AsStructuredType() + if mIdx := data.getMembersIndex(); mIdx == nil || mIdx.fallback == nil { + c.releaseSymbolTable(m) + } } func (c *Checker) getRequiresScopeChangeCache(node *ast.Node) core.Tristate { @@ -1835,7 +1909,7 @@ var primitiveTypeAliasSuggestions = sync.OnceValue(func() map[string]*ast.Symbol func getPrimitiveTypeAliasSuggestions(symbols ast.SymbolTable) iter.Seq[*ast.Symbol] { return func(yield func(*ast.Symbol) bool) { for builtinName, suggestion := range primitiveTypeAliasSuggestions() { - if _, ok := symbols[builtinName]; ok { + if _, ok := symbols.Lookup(builtinName); ok { if !yield(suggestion) { return } @@ -1853,7 +1927,7 @@ func (c *Checker) getSuggestionForSymbolNameLookup(symbols ast.SymbolTable, name if meaning&ast.SymbolFlagsGlobalLookup != 0 { extras = getPrimitiveTypeAliasSuggestions(symbols) } - return c.getSpellingSuggestionForName(name, core.ConcatenateSeq(maps.Values(symbols), extras), meaning) + return c.getSpellingSuggestionForName(name, core.ConcatenateSeq(symbols.Values(), extras), meaning) } // Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is @@ -2232,7 +2306,7 @@ func (c *Checker) getImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { if node == nil { panic("Unexpected nil in getImmediateAliasedSymbol") } - links.immediateTarget = c.getTargetOfAliasDeclaration(node) + links.immediateTarget = c.getTargetOfAliasDeclaration(node, true /*dontRecursivelyResolve*/) } return links.immediateTarget } @@ -2247,14 +2321,23 @@ func (c *Checker) addTypeOnlyDeclarationRelatedInfo(diagnostic *ast.Diagnostic, func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { if meaning&ast.SymbolFlagsAll != 0 { - symbol := c.getMergedSymbol(symbols[name]) + if s := symbols.Get(name); s != nil { + return c.resolveSymbolLookup(s, meaning) + } + } + return nil +} + +func (c *Checker) resolveSymbolLookup(s *ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol { + if meaning&ast.SymbolFlagsAll != 0 { + symbol := c.getMergedSymbol(s) if symbol != nil { if symbol.Flags()&meaning != 0 { return symbol } if symbol.Flags()&ast.SymbolFlagsAlias != 0 { + // 'targetFlags' will be 'SymbolFlags.All' if an error occurred in alias resolution; this avoids cascading errors targetFlags := c.getSymbolFlags(symbol) - // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors if targetFlags&meaning != 0 { return symbol } @@ -2286,6 +2369,7 @@ func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFil } c.produceDeferredDiagnostics() c.reportedUnreachableNodes.Clear() + c.flowReachableGen += 2 links.typeChecked = true } if checkUnused && !links.unusedChecked { @@ -2316,6 +2400,7 @@ func (c *Checker) checkSourceElement(node *ast.Node) bool { saveWithinUnreachableCode := c.withinUnreachableCode c.currentNode = node c.instantiationCount = 0 + c.instantiationCountResets++ c.checkSourceElementWorker(node) c.currentNode = saveCurrentNode c.withinUnreachableCode = saveWithinUnreachableCode @@ -2579,6 +2664,7 @@ func (c *Checker) checkDeferredNode(node *ast.Node) { saveCurrentNode := c.currentNode c.currentNode = node c.instantiationCount = 0 + c.instantiationCountResets++ switch node.Kind { case ast.KindCallExpression, ast.KindNewExpression, ast.KindTaggedTemplateExpression, ast.KindDecorator, ast.KindJsxOpeningElement: // These node kinds are deferred checked when overload resolution fails. To save on work, @@ -2893,7 +2979,7 @@ func (c *Checker) checkClassStaticBlockDeclaration(node *ast.Node) { // Grammar checking c.checkGrammarModifiers(node) node.ForEachChild(c.checkSourceElement) - if len(node.Locals()) != 0 { + if node.Locals().Len() != 0 { c.registerForUnusedIdentifiersCheck(node) } } @@ -3852,7 +3938,7 @@ func (c *Checker) checkBlock(node *ast.Node) { } else { c.checkSourceElements(node.Statements()) } - if len(node.Locals()) != 0 { + if node.Locals().Len() != 0 { c.registerForUnusedIdentifiersCheck(node) } } @@ -4317,8 +4403,8 @@ func (c *Checker) checkCatchClause(node *ast.Node) { } else { blockLocals := node.AsCatchClause().Block.Locals() if blockLocals != nil { - for caughtName := range node.Locals() { - if blockLocal := blockLocals[caughtName]; blockLocal != nil && blockLocal.ValueDeclaration() != nil && blockLocal.Flags()&ast.SymbolFlagsBlockScopedVariable != 0 { + for caughtName := range node.Locals().All() { + if blockLocal := blockLocals.Get(caughtName); blockLocal != nil && blockLocal.ValueDeclaration() != nil && blockLocal.Flags()&ast.SymbolFlagsBlockScopedVariable != 0 { c.grammarErrorOnNode(blockLocal.ValueDeclaration(), diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName) } } @@ -4588,12 +4674,16 @@ func (c *Checker) issueMemberSpecificError(node *ast.Node, typeWithThis *Type, b func (c *Checker) getTypeWithoutSignatures(t *Type) *Type { switch { case t.flags&TypeFlagsObject != 0: - resolved := c.resolveStructuredTypeMembers(t) - if len(resolved.getSignatures()) != 0 { + if c.typeHasCallOrConstructSignatures(t) { + resolved := c.resolveStructuredTypeMembers(t) result := c.newObjectType(ObjectFlagsAnonymous, t.symbol) result.objectFlags |= ObjectFlagsMembersResolved - result.AsObjectType().members = resolved.getMembers() - result.AsObjectType().properties = resolved.getProperties() + props := resolved.getProperties() + mIdx := resolved.getMembersIndex() + if mIdx == nil && len(props) != 0 { + mIdx = c.newPropertyIndex(props, nil) + } + result.AsObjectType().setMembersAndProperties(mIdx, props) return result } case t.flags&TypeFlagsIntersection != 0: @@ -5149,7 +5239,7 @@ func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.No return true } seen := make(map[string]InheritanceInfo) - for id, p := range c.resolveDeclaredMembers(t).declaredMembers { + for id, p := range c.resolveDeclaredMembers(t).declaredMembers.All() { if c.isNamedMember(p, id) { seen[p.Name()] = InheritanceInfo{prop: p, containingType: t} } @@ -5603,11 +5693,11 @@ func (c *Checker) checkImportAttributesExpression(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) if links.resolvedType == nil { symbol := c.newSymbol(ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameImportAttributes) - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for _, attribute := range node.AsImportAttributes().Attributes.Nodes { member := c.newSymbol(ast.SymbolFlagsProperty, attribute.Name().Text()) c.valueSymbolLinks.Get(member).resolvedType = c.getRegularTypeOfLiteralType(c.checkExpressionCached(attribute.AsImportAttribute().Value)) - members[member.Name()] = member + members.Set(member.Name(), member) } t := c.newAnonymousType(symbol, members, nil, nil, nil) t.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsNonInferrableType @@ -5882,7 +5972,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { moduleSymbol := c.getSymbolOfDeclaration(node) links := c.moduleSymbolLinks.Get(moduleSymbol) if !links.exportsChecked { - exportEqualsSymbol := moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals] + exportEqualsSymbol := moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) // An export assignment is in error if (a) the module exports value members or (b) if the module exports type or // namespace members and the exported entity also exports type or namespace members. if exportEqualsSymbol != nil && (c.hasExportedMembersOfKind(moduleSymbol, ast.SymbolFlagsValue) || c.hasShadowedNamespace(exportEqualsSymbol)) { @@ -5892,7 +5982,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } } // Checks for export * conflicts - for id, symbol := range c.getExportsOfModule(moduleSymbol) { + for id, symbol := range c.getExportsOfModule(moduleSymbol).All() { if id == ast.InternalSymbolNameExportStar { continue } @@ -5924,7 +6014,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } func (c *Checker) hasExportedMembersOfKind(moduleSymbol *ast.Symbol, kind ast.SymbolFlags) bool { - for _, symbol := range moduleSymbol.Exports() { + for _, symbol := range moduleSymbol.Exports().All() { if symbol.Name() != ast.InternalSymbolNameExportEquals && c.getSymbolFlags(symbol)&kind != 0 { return true } @@ -6009,6 +6099,9 @@ func (c *Checker) checkVariableLikeDeclaration(node *ast.Node) { // variable renaming in function type notation is confusing, // so we forbid it even if noUnusedLocals is not enabled c.renamedBindingElementsInTypes = append(c.renamedBindingElementsInTypes, node) + // Still resolve the type like the end of this function does for every other declaration, + // so implicit any errors are reported by checking instead of by whatever asks for the type later + c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) return } if ast.IsObjectBindingPattern(node.Parent) && hasDotDotDotToken(node) && c.languageVersion < LanguageFeatureMinimumTarget.ObjectSpreadRest { @@ -6779,8 +6872,8 @@ func (c *Checker) getIterationTypesOfMethod(t *Type, resolver *IterationTypesRes if len(methodSignatures) == 1 && methodType.symbol != nil { globalGeneratorType := resolver.getGlobalGeneratorType() globalIteratorType := resolver.getGlobalIteratorType() - isGeneratorMethod := globalGeneratorType.symbol != nil && globalGeneratorType.symbol.Members()[methodName] == methodType.symbol - isIteratorMethod := !isGeneratorMethod && globalIteratorType.symbol != nil && globalIteratorType.symbol.Members()[methodName] == methodType.symbol + isGeneratorMethod := globalGeneratorType.symbol != nil && globalGeneratorType.symbol.Members().Get(methodName) == methodType.symbol + isIteratorMethod := !isGeneratorMethod && globalIteratorType.symbol != nil && globalIteratorType.symbol.Members().Get(methodName) == methodType.symbol if isGeneratorMethod || isIteratorMethod { typeParameters := core.IfElse(isGeneratorMethod, globalGeneratorType, globalIteratorType).AsInterfaceType().TypeParameters() mapper := methodType.Mapper() @@ -6956,7 +7049,7 @@ func (c *Checker) checkAliasSymbol(node *ast.Node) { if ast.IsExportSpecifier(node) { diag := c.error(errorNode, diagnostics.Types_cannot_appear_in_export_declarations_in_JavaScript_files) if sourceSymbol := ast.GetSourceFileOfNode(node).AsNode().Symbol(); sourceSymbol != nil { - if alreadyExportedSymbol := sourceSymbol.Exports()[node.PropertyNameOrName().Text()]; alreadyExportedSymbol == target { + if alreadyExportedSymbol := sourceSymbol.Exports().Get(node.PropertyNameOrName().Text()); alreadyExportedSymbol == target { if exportingDeclaration := core.Find(alreadyExportedSymbol.Declarations(), ast.IsJSTypeAliasDeclaration); exportingDeclaration != nil { diag.AddRelatedInfo(NewDiagnosticForNode(exportingDeclaration, diagnostics.X_0_is_automatically_exported_here, alreadyExportedSymbol.Name())) } @@ -7273,8 +7366,12 @@ func (c *Checker) checkUnusedIdentifiers(potentiallyUnusedIdentifiers []*ast.Nod } } +func (c *Checker) getReferenceKinds(symbol *ast.Symbol) ast.SymbolFlags { + return symbol.ReferenceKinds() +} + func (c *Checker) isReferenced(symbol *ast.Symbol) bool { - return c.symbolReferenceLinks.Get(symbol).referenceKinds != 0 + return c.getReferenceKinds(symbol) != 0 } type UnusedKind int32 @@ -7343,8 +7440,8 @@ func (c *Checker) checkUnusedClassMembers(node *ast.Node) { func (c *Checker) checkUnusedLocalsAndParameters(node *ast.Node) { var variableParents collections.Set[*ast.Node] var importClauses map[*ast.Node][]*ast.Node - for _, local := range node.Locals() { - referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds + for local := range node.Locals().Values() { + referenceKinds := c.getReferenceKinds(local) if local.Flags()&ast.SymbolFlagsTypeParameter != 0 && (local.Flags()&ast.SymbolFlagsVariable == 0 || referenceKinds&ast.SymbolFlagsVariable != 0) || local.Flags()&ast.SymbolFlagsTypeParameter == 0 && (referenceKinds != 0 || local.ExportSymbol() != nil || local.Flags()&ast.SymbolFlagsModuleExports != 0) { @@ -7429,7 +7526,7 @@ func (c *Checker) isUnreferencedVariableDeclaration(node *ast.Node) bool { if ast.IsBindingPattern(name) { return core.Every(node.Name().Elements(), c.isUnreferencedVariableDeclaration) } - if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds&ast.SymbolFlagsVariable != 0 { + if c.getReferenceKinds(c.getSymbolOfDeclaration(node))&ast.SymbolFlagsVariable != 0 { return false } if ast.IsBindingElement(node) && ast.IsObjectBindingPattern(node.Parent) { @@ -7511,12 +7608,12 @@ func (c *Checker) checkUnusedTypeParameters(node *ast.Node) { } func (c *Checker) isUnreferencedTypeParameter(typeParameter *ast.Node) bool { - return c.symbolReferenceLinks.Get(c.getMergedSymbol(typeParameter.Symbol())).referenceKinds&ast.SymbolFlagsTypeParameter == 0 && !isIdentifierThatStartsWithUnderscore(typeParameter.Name()) + return c.getReferenceKinds(c.getMergedSymbol(typeParameter.Symbol()))&ast.SymbolFlagsTypeParameter == 0 && !isIdentifierThatStartsWithUnderscore(typeParameter.Name()) } func (c *Checker) checkUnusedRenamedBindingElements() { for _, node := range c.renamedBindingElementsInTypes { - if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds == 0 { + if c.getReferenceKinds(c.getSymbolOfDeclaration(node)) == 0 { wrappingDeclaration := ast.WalkUpBindingElementsAndPatterns(node) debug.Assert(ast.IsPartOfParameterDeclaration(wrappingDeclaration), "Only parameter declaration should be checked here") diagnostic := NewDiagnosticForNode(node.Name(), diagnostics.X_0_is_an_unused_renaming_of_1_Did_you_intend_to_use_it_as_a_type_annotation, scanner.DeclarationNameToString(node.Name()), scanner.DeclarationNameToString(node.PropertyName())) @@ -7545,6 +7642,8 @@ func (c *Checker) getTypeOfExpression(node *ast.Node) *Type { } // If a type has been cached for the node, return it. if cachedType := c.flowTypeCache[node]; cachedType != nil { + c.resolutionCycleCount++ + c.flowMemo.addFlags(flowMemoFlagTypeCache) return cachedType } startInvocationCount := c.flowInvocationCount @@ -7555,6 +7654,7 @@ func (c *Checker) getTypeOfExpression(node *ast.Node) *Type { c.flowTypeCache = make(map[*ast.Node]*Type) } c.flowTypeCache[node] = t + c.flowMemo.typeCacheEpoch = c.flowMemo.nextSerial() } return t } @@ -7724,14 +7824,11 @@ func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) * links := c.typeNodeLinks.Get(node) if links.resolvedType == nil { // When computing a type that we're going to cache, we need to ignore any ongoing control flow - // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart - // to the top of the stack ensures all transient types are computed from a known point. saveFlowLoopStack := c.flowLoopStack - saveFlowTypeCache := c.flowTypeCache + saveFlowTypeCache := c.takeFlowTypeCache() c.flowLoopStack = nil - c.flowTypeCache = nil links.resolvedType = c.checkExpressionEx(node, checkMode) - c.flowTypeCache = saveFlowTypeCache + c.restoreFlowTypeCache(saveFlowTypeCache) c.flowLoopStack = saveFlowLoopStack } return links.resolvedType @@ -7764,6 +7861,7 @@ func (c *Checker) checkExpressionEx(node *ast.Node, checkMode CheckMode) *Type { saveCurrentNode := c.currentNode c.currentNode = node c.instantiationCount = 0 + c.instantiationCountResets++ uninstantiatedType := c.checkExpressionWorker(node, checkMode) t := c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode) if isConstEnumObjectType(t) { @@ -7821,6 +7919,10 @@ func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *ast.Node, c.skippedGenericFunction(node, checkMode) return c.anyFunctionType } + return c.instantiateTypeWithSingleGenericCallSignatureWorker(node, t, signature, contextualSignature) +} + +func (c *Checker) instantiateTypeWithSingleGenericCallSignatureWorker(node *ast.Node, t *Type, signature *Signature, contextualSignature *Signature) *Type { context := c.getInferenceContext(node) // We have an expression that is an argument of a generic function for which we are performing // type argument inference. The expression is of a function type with a single generic call @@ -7829,7 +7931,7 @@ func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *ast.Node, // if some of the outer function type parameters have no inferences so far. If so, we can // potentially add inferred type parameters to the outer function return type. var returnSignature *Signature - if context.signature != nil { + if context != nil && context.signature != nil { returnType := c.getReturnTypeOfSignature(context.signature) if returnType != nil { returnSignature = c.getSingleCallOrConstructSignature(returnType) @@ -7842,9 +7944,10 @@ func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *ast.Node, instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(signature, uniqueTypeParameters) // Infer from the parameters of the instantiated signature to the parameters of the // contextual signature starting with an empty set of inference candidates. - inferences := core.Map(context.inferences, func(info *InferenceInfo) *InferenceInfo { - return newInferenceInfo(info.typeParameter) - }) + inferences := make([]*InferenceInfo, len(context.inferences)) + for i, info := range context.inferences { + inferences[i] = newInferenceInfo(info.typeParameter) + } c.applyToParameterTypes(instantiatedSignature, contextualSignature, func(source *Type, target *Type) { c.inferTypes(inferences, source, target, InferencePriorityNone, true /*contravariant*/) }) @@ -7907,6 +8010,7 @@ func (c *Checker) getUniqueTypeParameters(context *InferenceContext, typeParamet for _, tp := range newTypeParameters { tp.AsTypeParameter().mapper = mapper } + markMapperEscaped(mapper) } return result } @@ -8624,24 +8728,22 @@ func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Si return cached } saveResolutionStart := c.resolutionStart + // If we haven't already done so, temporarily reset the resolution stack. This allows us to + // handle "inverted" situations where, for example, an API client asks for the type of a symbol + // containined in a function call argument whose contextual type depends on the symbol itself + // through resolution of the containing function call. By resetting the resolution stack we'll + // retry the symbol type resolution with the resolvingSignature marker in place to suppress + // the contextual type circularity. if cached == nil { - // If we haven't already done so, temporarily reset the resolution stack. This allows us to - // handle "inverted" situations where, for example, an API client asks for the type of a symbol - // containined in a function call argument whose contextual type depends on the symbol itself - // through resolution of the containing function call. By resetting the resolution stack we'll - // retry the symbol type resolution with the resolvingSignature marker in place to suppress - // the contextual type circularity. c.resolutionStart = len(c.typeResolutions) + } else if cached == c.resolvingSignature { + c.resolutionCycleCount++ + c.flowMemo.taint(0) } links.resolvedSignature = c.resolvingSignature result := c.resolveSignature(node, candidatesOutArray, checkMode) c.resolutionStart = saveResolutionStart - // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call - // resolution should be deferred. if result != c.resolvingSignature { - // if the signature resolution originated on a node that itself depends on the contextual type - // then it's possible that the resolved signature might not be the same as the one that would be computed in source order - // since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature // it's possible that this inner resolution sets the resolvedSignature first. // In such a case we ignore the local result and reuse the correct one that was cached. if links.resolvedSignature != c.resolvingSignature { @@ -9060,13 +9162,44 @@ type CallState struct { candidateForTypeArgumentError *Signature } +func (c *Checker) acquireCallState() *CallState { + if n := len(c.freeCallStates); n > 0 { + s := c.freeCallStates[n-1] + c.freeCallStates = c.freeCallStates[:n-1] + return s + } + return &CallState{} +} + +func (c *Checker) releaseCallState(s *CallState, poolCandidates bool) { + var candidates []*Signature + if poolCandidates && cap(s.candidates) <= 64 { + clear(s.candidates) + candidates = s.candidates[:0] + } + var candidatesForArgumentError []*Signature + if cap(s.candidatesForArgumentError) <= 64 { + clear(s.candidatesForArgumentError) + candidatesForArgumentError = s.candidatesForArgumentError[:0] + } + *s = CallState{ + candidates: candidates, + candidatesForArgumentError: candidatesForArgumentError, + } + c.freeCallStates = append(c.freeCallStates, s) +} + +func (c *Checker) someContextSensitive(args []*ast.Node) bool { + return slices.ContainsFunc(args, c.isContextSensitive) +} + func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidatesOutArray *[]*Signature, checkMode CheckMode, callChainFlags SignatureFlags, headMessage *diagnostics.Message) *Signature { isTaggedTemplate := node.Kind == ast.KindTaggedTemplateExpression isDecorator := node.Kind == ast.KindDecorator isJsxOpeningOrSelfClosingElement := ast.IsJsxOpeningLikeElement(node) isInstanceof := node.Kind == ast.KindBinaryExpression reportErrors := !c.isInferencePartiallyBlocked && candidatesOutArray == nil - var s CallState + s := c.acquireCallState() s.node = node if !isDecorator && !isInstanceof && !isSuperCall(node) && !ast.IsJsxOpeningFragment(node) { s.typeArguments = node.TypeArguments() @@ -9075,14 +9208,16 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate c.checkSourceElements(s.typeArguments) } } - s.candidates = c.reorderCandidates(signatures, callChainFlags) if candidatesOutArray != nil { + s.candidates = c.reorderCandidatesInto(nil, signatures, callChainFlags) *candidatesOutArray = s.candidates + } else { + s.candidates = c.reorderCandidatesInto(s.candidates[:0], signatures, callChainFlags) } - if len(s.candidates) == 0 { // In Strada we would error here, but no known repro doesn't have at least // one other error in this codepath. Just return instead. See #54442 + c.releaseCallState(s, candidatesOutArray == nil) return c.unknownSignature } @@ -9100,7 +9235,7 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. s.isSingleNonGenericCandidate = len(s.candidates) == 1 && len(s.candidates[0].TypeParameters()) == 0 - if !isDecorator && !s.isSingleNonGenericCandidate && core.Some(s.args, c.isContextSensitive) { + if !isDecorator && !s.isSingleNonGenericCandidate && c.someContextSensitive(s.args) { s.argCheckMode = CheckModeSkipContextSensitive } else { s.argCheckMode = CheckModeNormal @@ -9143,13 +9278,14 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate s.recursiveResolution = slices.Contains(c.callResolutionStack, s.node) c.callResolutionStack = append(c.callResolutionStack, s.node) if len(s.candidates) > 1 { - result = c.chooseOverload(&s, c.subtypeRelation) + result = c.chooseOverload(s, c.subtypeRelation) } if result == nil { - result = c.chooseOverload(&s, c.assignableRelation) + result = c.chooseOverload(s, c.assignableRelation) } c.callResolutionStack = c.callResolutionStack[:len(c.callResolutionStack)-1] if result != nil { + c.releaseCallState(s, candidatesOutArray == nil) return result } result = c.getCandidateForOverloadFailure(s.node, s.candidates, s.args, candidatesOutArray != nil, checkMode) @@ -9172,19 +9308,39 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate if headMessage == nil && isInstanceof { headMessage = diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_assignable_to_the_first_argument_of_the_right_hand_side_s_Symbol_hasInstance_method } - c.reportCallResolutionErrors(node, &s, signatures, headMessage) + c.reportCallResolutionErrors(node, s, signatures, headMessage) } + c.releaseCallState(s, candidatesOutArray == nil) return result } func (c *Checker) reorderCandidates(signatures []*Signature, callChainFlags SignatureFlags) []*Signature { + return c.reorderCandidatesInto(nil, signatures, callChainFlags) +} + +func (c *Checker) reorderCandidatesInto(dst []*Signature, signatures []*Signature, callChainFlags SignatureFlags) []*Signature { + if len(signatures) == 0 { + return dst[:0] + } + if len(signatures) == 1 { + sig := signatures[0] + if callChainFlags != 0 { + sig = c.getOptionalCallSignature(sig, callChainFlags) + } + return append(dst[:0], sig) + } var lastParent *ast.Node var lastSymbol *ast.Symbol var index int var cutoffIndex int var spliceIndex int specializedIndex := -1 - result := make([]*Signature, 0, len(signatures)) + var result []*Signature + if cap(dst) >= len(signatures) { + result = dst[:0] + } else { + result = make([]*Signature, 0, len(signatures)) + } for _, signature := range signatures { var symbol *ast.Symbol var parent *ast.Node @@ -9246,7 +9402,8 @@ func (c *Checker) getOptionalCallSignature(signature *Signature, callChainFlags } func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { - s.candidatesForArgumentError = nil + clear(s.candidatesForArgumentError) + s.candidatesForArgumentError = s.candidatesForArgumentError[:0] s.candidateForArgumentArityError = nil s.candidateForTypeArgumentError = nil if s.isSingleNonGenericCandidate { @@ -9255,7 +9412,7 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { return nil } if !c.isSignatureApplicable(s.node, s.args, candidate, relation, CheckModeNormal, false /*reportErrors*/, nil /*diagnosticOutput*/) { - s.candidatesForArgumentError = []*Signature{candidate} + s.candidatesForArgumentError = append(s.candidatesForArgumentError[:0], candidate) return nil } return candidate @@ -9294,6 +9451,9 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { // signature with different arity and we need to perform another arity check. if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) { s.candidateForArgumentArityError = checkCandidate + if inferenceContext != nil && !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } continue } } else { @@ -9302,6 +9462,9 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, nil /*diagnosticOutput*/) { // Give preference to error candidates that have no rest parameters (as they are more specific) s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate) + if inferenceContext != nil && !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } continue } if s.argCheckMode != 0 { @@ -9316,15 +9479,24 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { // signature with different arity and we need to perform another arity check. if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) { s.candidateForArgumentArityError = checkCandidate + if !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } continue } } if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, nil /*diagnosticOutput*/) { // Give preference to error candidates that have no rest parameters (as they are more specific) s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate) + if inferenceContext != nil && !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } continue } } + if inferenceContext != nil && !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } s.candidates[candidateIndex] = checkCandidate return checkCandidate } @@ -9683,6 +9855,9 @@ func (c *Checker) inferTypeArguments(node *ast.Node, signature *Signature, args } else { context.returnMapper = nil } + if !returnContext.escaped { + c.releaseConditionalInferenceContext(returnContext, nil, nil) + } } } } @@ -9802,11 +9977,14 @@ func (c *Checker) getTypeArgumentsFromNodes(typeArgumentNodes []*ast.Node, typeP func (c *Checker) inferSignatureInstantiationForOverloadFailure(node *ast.Node, typeParameters []*Type, candidate *Signature, args []*ast.Node, checkMode CheckMode) *Signature { inferenceContext := c.newInferenceContext(typeParameters, candidate, core.IfElse(ast.IsInJSFile(node), InferenceFlagsAnyDefault, InferenceFlagsNone), nil) typeArgumentTypes := c.inferTypeArguments(node, candidate, args, checkMode|CheckModeSkipContextSensitive|CheckModeSkipGenericFunctions, inferenceContext) + if !inferenceContext.escaped { + c.releaseConditionalInferenceContext(inferenceContext, nil, nil) + } return c.createSignatureInstantiation(candidate, typeArgumentTypes) } func (c *Checker) createUnionOfSignaturesForOverloadFailure(candidates []*Signature) *Signature { - thisParameters := core.MapNonNil(candidates, func(c *Signature) *ast.Symbol { return c.ThisParameter() }) + thisParameters := core.MapNonNil(candidates, func(s *Signature) *ast.Symbol { return c.getSignatureThisParameter(s) }) var thisParameter *ast.Symbol if len(thisParameters) != 0 { thisParameter = c.createCombinedSymbolFromTypes(thisParameters, core.Map(thisParameters, c.getTypeOfParameter)) @@ -9863,7 +10041,7 @@ func (c *Checker) tryGetRestTypeOfSignature(signature *Signature) *Type { if !signatureHasRestParameter(signature) { return nil } - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) + restType := c.getTypeOfSignatureParameterSymbol(signature, len(signature.parameters)-1) if isTupleType(restType) { restType = c.getRestTypeOfTupleType(restType) if restType == nil { @@ -10593,7 +10771,7 @@ func (c *Checker) assignContextualParameterTypes(sig *Signature, context *Signat if parameter == nil { sig.setThisParameter(c, c.createSymbolWithType(context.ThisParameter(), nil /*type*/)) } - c.assignParameterType(sig.ThisParameter(), c.getTypeOfSymbol(context.ThisParameter())) + c.assignParameterType(sig.ThisParameter(), c.getThisTypeOfSignature(context)) } } length := len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0) @@ -11573,7 +11751,7 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l return c.anyType } if leftType.symbol == c.globalThisSymbol { - globalSymbol := c.globalThisSymbol.Exports()[right.Text()] + globalSymbol := c.globalThisSymbol.Exports().Get(right.Text()) if globalSymbol != nil && globalSymbol.Flags()&ast.SymbolFlagsBlockScoped != 0 { c.error(right, diagnostics.Property_0_does_not_exist_on_type_1, right.Text(), c.TypeToString(leftType)) } else if c.noImplicitAny { @@ -11714,11 +11892,11 @@ func (c *Checker) lookupSymbolForPrivateIdentifierDeclaration(propName string, l for containingClass := getContainingClassExcludingClassDecorators(location); containingClass != nil; containingClass = ast.GetContainingClass(containingClass) { symbol := containingClass.Symbol() name := binder.GetSymbolNameForPrivateIdentifier(symbol, propName) - prop := symbol.Members()[name] + prop := symbol.Members().Get(name) if prop != nil { return prop } - prop = symbol.Exports()[name] + prop = symbol.Exports().Get(name) if prop != nil { return prop } @@ -12148,7 +12326,7 @@ func (c *Checker) forEachProperty(prop *ast.Symbol, callback func(p *ast.Symbol) if prop.CheckFlags()&ast.CheckFlagsSynthetic == 0 { return callback(prop) } - for _, t := range c.valueSymbolLinks.Get(prop).getContainingType().Types() { + for _, t := range c.valueSymbolLinks.TryGet(prop).getContainingType().Types() { p := c.getPropertyOfType(t, prop.Name()) if p != nil && c.forEachProperty(p, callback) { return true @@ -12266,9 +12444,8 @@ func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { if c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) { contextualSignature := c.getContextualSignature(fn) if contextualSignature != nil { - thisParameter := contextualSignature.ThisParameter() - if thisParameter != nil { - return c.getTypeOfSymbol(thisParameter) + if thisType := c.getThisTypeOfSignature(contextualSignature); thisType != nil { + return thisType } } } @@ -13004,7 +13181,7 @@ func (c *Checker) checkAssignmentOperator(left *ast.Node, operator ast.Kind, rig if ast.IsAssignmentOperator(operator) { // We ignore assignments of undefined to CommonJS exports when there are multiple assignment declarations if ast.IsDeclarationNode(left.Parent) && ast.GetAssignmentDeclarationKind(left.Parent) == ast.JSDeclarationKindExportsProperty { - if symbol := c.symbolNodeLinks.Get(left).resolvedSymbol; symbol != nil && len(symbol.Declarations()) > 1 && rightType.flags&TypeFlagsUndefined != 0 { + if links := c.symbolNodeLinks.TryGet(left); links != nil && links.resolvedSymbol != nil && len(links.resolvedSymbol.Declarations()) > 1 && rightType.flags&TypeFlagsUndefined != 0 { return } } @@ -13332,7 +13509,7 @@ func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type // which provides us with the opportunity to emit more detailed errors - if c.symbolNodeLinks.Get(left).resolvedSymbol == nil && ast.GetContainingClass(left) != nil { + if c.getResolvedSymbolOrNil(left) == nil && ast.GetContainingClass(left) != nil { isUncheckedJS := c.isUncheckedJSSuggestion(left, rightType.symbol, true /*excludeClasses*/) c.reportNonexistentProperty(left, rightType, isUncheckedJS) } @@ -13389,7 +13566,7 @@ func (c *Checker) checkReferenceExpression(expr *ast.Node, invalidReferenceMessa func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type { // Expando object literals have empty properties but filled exports - if len(node.Properties()) == 0 && node.Symbol() != nil && len(node.Symbol().Exports()) != 0 { + if len(node.Properties()) == 0 && node.Symbol() != nil && node.Symbol().Exports().Len() != 0 { result := c.newAnonymousType(node.Symbol(), node.Symbol().Exports(), nil, nil, nil) if ast.IsInJSFile(node) && !ast.IsInJsonFile(node) { result.objectFlags |= ObjectFlagsJSLiteral @@ -13403,10 +13580,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type // Grammar checking c.checkGrammarObjectLiteralExpression(node.AsObjectLiteralExpression(), inDestructuringPattern) var allPropertiesTable ast.SymbolTable - if c.strictNullChecks { - allPropertiesTable = make(ast.SymbolTable) - } - propertiesTable := make(ast.SymbolTable) + propertiesTable := c.acquireSymbolTable() var propertiesArray []*ast.Symbol spread := c.emptyObjectType c.pushCachedContextualType(node) @@ -13431,36 +13605,14 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type // As otherwise they may not be checked until exports for the type at this position are retrieved, // which may never occur. for _, elem := range node.Properties() { + if elem.Kind == ast.KindSpreadAssignment && c.strictNullChecks && allPropertiesTable == nil { + allPropertiesTable = c.acquireSymbolTable() + } if elem.Name() != nil && ast.IsComputedPropertyName(elem.Name()) { c.checkComputedPropertyName(elem.Name()) } } offset := 0 - createObjectLiteralType := func() *Type { - var indexInfos []*IndexInfo - isReadonly := c.isConstContext(node) - if hasComputedStringProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.stringType)) - } - if hasComputedNumberProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.numberType)) - } - if hasComputedSymbolProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.esSymbolType)) - } - result := c.newAnonymousType(node.Symbol(), propertiesTable, nil, nil, indexInfos) - result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral - if contextualType == nil && ast.IsInJSFile(node) && !ast.IsInJsonFile(node) { - result.objectFlags |= ObjectFlagsJSLiteral - } - if patternWithComputedProperties { - result.objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties - } - if inDestructuringPattern { - c.patternForType[result] = node - } - return result - } for _, memberDecl := range node.Properties() { member := c.getSymbolOfDeclaration(memberDecl) var computedNameType *Type @@ -13485,8 +13637,13 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type var prop *ast.Symbol if nameType != nil { prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags(), getPropertyNameFromType(nameType), checkFlags|ast.CheckFlagsLate) + prop.SetDeclarations(member.Declarations()) + prop.SetParent(member.Parent()) + prop.SetValueDeclaration(member.ValueDeclaration()) } else { - prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags(), member.Name(), checkFlags) + prop = c.newSharedDataSymbol(member) + prop.SetFlags(prop.Flags() | ast.SymbolFlagsProperty) + prop.SetCheckFlags(checkFlags) } links := c.valueSymbolLinks.Get(prop) if nameType != nil { @@ -13506,14 +13663,11 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type c.error(memberDecl.Name(), diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, c.symbolToString(member), c.TypeToString(contextualType)) } } - prop.SetDeclarations(member.Declarations()) - prop.SetParent(member.Parent()) - prop.SetValueDeclaration(member.ValueDeclaration()) links.resolvedType = t links.target = member member = prop if allPropertiesTable != nil { - allPropertiesTable[prop.Name()] = prop + allPropertiesTable.Set(prop.Name(), prop) } if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && (ast.IsPropertyAssignment(memberDecl) || ast.IsMethodDeclaration(memberDecl)) && c.isContextSensitive(memberDecl) { inferenceContext := c.getInferenceContext(node) @@ -13526,9 +13680,11 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type } } else if memberDecl.Kind == ast.KindSpreadAssignment { if len(propertiesArray) > 0 { - spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) + objLitType := c.createObjectLiteralType(node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + c.releaseSymbolTableIfUnused(objLitType, propertiesTable) + spread = c.getSpreadType(spread, objLitType, node.Symbol(), objectFlags, inConstContext) propertiesArray = nil - propertiesTable = make(ast.SymbolTable) + propertiesTable = c.acquireSymbolTable() hasComputedStringProperty = false hasComputedNumberProperty = false hasComputedSymbolProperty = false @@ -13572,31 +13728,81 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type } } } else { - propertiesTable[member.Name()] = member + propertiesTable.Set(member.Name(), member) } propertiesArray = append(propertiesArray, member) } c.popContextualType() if c.isErrorType(spread) { + c.releaseSymbolTable(propertiesTable) return c.errorType } if spread != c.emptyObjectType { if len(propertiesArray) > 0 { - spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) + objLitType := c.createObjectLiteralType(node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + c.releaseSymbolTableIfUnused(objLitType, propertiesTable) + spread = c.getSpreadType(spread, objLitType, node.Symbol(), objectFlags, inConstContext) propertiesArray = nil - propertiesTable = make(ast.SymbolTable) + propertiesTable = nil hasComputedStringProperty = false hasComputedNumberProperty = false + hasComputedSymbolProperty = false + } else { + c.releaseSymbolTable(propertiesTable) + propertiesTable = nil } // remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site - return c.mapType(spread, func(t *Type) *Type { - if t == c.emptyObjectType { - return createObjectLiteralType() - } - return t - }) + return c.remapSpreadEmptyObjectType(spread, node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + } + res := c.createObjectLiteralType(node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + c.releaseSymbolTableIfUnused(res, propertiesTable) + return res +} + +func (c *Checker) createObjectLiteralType(node *ast.Node, objectFlags ObjectFlags, propertiesTable ast.SymbolTable, propertiesArray []*ast.Symbol, offset int, contextualType *Type, inConstContext bool, hasComputedStringProperty bool, hasComputedNumberProperty bool, hasComputedSymbolProperty bool, patternWithComputedProperties bool, inDestructuringPattern bool) *Type { + var indexInfos []*IndexInfo + isReadonly := inConstContext + if hasComputedStringProperty { + indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.stringType)) + } + if hasComputedNumberProperty { + indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.numberType)) + } + if hasComputedSymbolProperty { + indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.esSymbolType)) + } + var result *Type + if propertiesTable.Len() == 0 { + result = c.newAnonymousType(node.Symbol(), nil, nil, nil, indexInfos) + } else { + result = c.newAnonymousType(node.Symbol(), propertiesTable, nil, nil, indexInfos) + } + result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral + if contextualType == nil && ast.IsInJSFile(node) && !ast.IsInJsonFile(node) { + result.objectFlags |= ObjectFlagsJSLiteral + } + if patternWithComputedProperties { + result.objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties } - return createObjectLiteralType() + if inDestructuringPattern { + c.patternForType[result] = node + } + return result +} + +func (c *Checker) remapSpreadEmptyObjectType(spread *Type, node *ast.Node, objectFlags ObjectFlags, propertiesTable ast.SymbolTable, propertiesArray []*ast.Symbol, offset int, contextualType *Type, inConstContext bool, hasComputedStringProperty bool, hasComputedNumberProperty bool, hasComputedSymbolProperty bool, patternWithComputedProperties bool, inDestructuringPattern bool) *Type { + if spread.flags&TypeFlagsUnion == 0 { + if spread == c.emptyObjectType { + return c.createObjectLiteralType(node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + } + return spread + } + return c.mapType(spread, func(t *Type) *Type { + if t == c.emptyObjectType { + return c.createObjectLiteralType(node, objectFlags, propertiesTable, propertiesArray, offset, contextualType, inConstContext, hasComputedStringProperty, hasComputedNumberProperty, hasComputedSymbolProperty, patternWithComputedProperties, inDestructuringPattern) + } + return t + }) } func (c *Checker) checkContextualDeprecations(node *ast.Node) { @@ -13627,7 +13833,7 @@ func (c *Checker) checkDeprecatedProperty(name *ast.Node, contextualType *Type) func (c *Checker) checkSpreadPropOverrides(t *Type, props ast.SymbolTable, spread *ast.Node) { for _, right := range c.getPropertiesOfType(t) { if right.Flags()&ast.SymbolFlagsOptional == 0 && right.CheckFlags()&ast.CheckFlagsPartial == 0 { - if left := props[right.Name()]; left != nil { + if left := props.Get(right.Name()); left != nil { diagnostic := c.error(left.ValueDeclaration(), diagnostics.X_0_is_specified_more_than_once_so_this_usage_will_be_overwritten, left.Name()) diagnostic.AddRelatedInfo(NewDiagnosticForNode(spread, diagnostics.This_spread_always_overwrites_this_property)) } @@ -13692,7 +13898,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj } return c.getIntersectionType([]*Type{left, right}) } - members := make(ast.SymbolTable) + members := c.acquireSymbolTable() var skippedPrivateMembers collections.Set[string] var indexInfos []*IndexInfo if left == c.emptyObjectType { @@ -13704,7 +13910,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj if getDeclarationModifierFlagsFromSymbol(rightProp)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { skippedPrivateMembers.Add(rightProp.Name()) } else if c.isSpreadableProperty(rightProp) { - members[rightProp.Name()] = c.getSpreadSymbol(rightProp, readonly) + members.Set(rightProp.Name(), c.getSpreadSymbol(rightProp, readonly)) } } @@ -13712,8 +13918,8 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj if skippedPrivateMembers.Has(leftProp.Name()) || !c.isSpreadableProperty(leftProp) { continue } - if members[leftProp.Name()] != nil { - rightProp := members[leftProp.Name()] + if members.Get(leftProp.Name()) != nil { + rightProp := members.Get(leftProp.Name()) rightType := c.getTypeOfSymbol(rightProp) if rightProp.Flags()&ast.SymbolFlagsOptional != 0 { declarations := core.Concatenate(leftProp.Declarations(), rightProp.Declarations()) @@ -13735,17 +13941,18 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj c.spreadLinks.Get(result).leftSpread = leftProp c.spreadLinks.Get(result).rightSpread = rightProp result.SetDeclarations(declarations) - links.setNameType(c, c.valueSymbolLinks.Get(leftProp).getNameType()) - members[leftProp.Name()] = result + links.setNameType(c, c.valueSymbolLinks.TryGet(leftProp).getNameType()) + members.Set(leftProp.Name(), result) } } else { - members[leftProp.Name()] = c.getSpreadSymbol(leftProp, readonly) + members.Set(leftProp.Name(), c.getSpreadSymbol(leftProp, readonly)) } } spreadIndexInfos := core.SameMap(indexInfos, func(info *IndexInfo) *IndexInfo { return c.getIndexInfoWithReadonly(info, readonly) }) spread := c.newAnonymousType(symbol, members, nil, nil, spreadIndexInfos) + c.releaseSymbolTableIfUnused(spread, members) spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsContainsSpread | objectFlags return spread } @@ -13807,7 +14014,7 @@ func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool return t } // gets the type as if it had been spread, but where everything in the spread is made optional - members := make(ast.SymbolTable) + members := c.acquireSymbolTable() for _, prop := range c.getPropertiesOfType(firstType) { if getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { // do nothing, skip privates @@ -13822,12 +14029,13 @@ func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool links.resolvedType = c.addOptionalityEx(c.getTypeOfSymbol(prop), true /*isProperty*/, true /*isOptional*/) } result.SetDeclarations(prop.Declarations()) - links.setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) - c.mappedSymbolLinks.Get(result).syntheticOrigin = prop - members[prop.Name()] = result + links.setNameType(c, c.valueSymbolLinks.TryGet(prop).getNameType()) + links.target = prop + members.Set(prop.Name(), result) } } spread := c.newAnonymousType(firstType.symbol, members, nil, nil, c.getIndexInfosOfType(firstType)) + c.releaseSymbolTableIfUnused(spread, members) spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral return spread } @@ -13852,8 +14060,8 @@ func (c *Checker) getSpreadSymbol(prop *ast.Symbol, readonly bool) *ast.Symbol { links.resolvedType = c.getTypeOfSymbol(prop) } result.SetDeclarations(prop.Declarations()) - links.setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) - c.mappedSymbolLinks.Get(result).syntheticOrigin = prop + links.setNameType(c, c.valueSymbolLinks.TryGet(prop).getNameType()) + links.target = prop return result } @@ -13869,12 +14077,37 @@ func (c *Checker) hasDefaultValue(node *ast.Node) bool { } func (c *Checker) isConstContext(node *ast.Node) bool { + return c.isConstContextEx(node, nil, nil) +} + +func (c *Checker) isConstContextEx(node *ast.Node, contextualTypeOut **Type, hasContextualTypeOut *bool) bool { parent := node.Parent - return ast.IsConstAssertion(parent) || - c.isInlineImportAttributes(node) || - c.isValidConstAssertionArgument(node) && c.isConstTypeVariable(c.getContextualType(node, ContextFlagsNone), 0) || - (ast.IsParenthesizedExpression(parent) || ast.IsArrayLiteralExpression(parent) || ast.IsSpreadElement(parent)) && c.isConstContext(parent) || - (ast.IsPropertyAssignment(parent) || ast.IsShorthandPropertyAssignment(parent) || ast.IsTemplateSpan(parent)) && c.isConstContext(parent.Parent) + if ast.IsConstAssertion(parent) || c.isInlineImportAttributes(node) { + return true + } + if c.isValidConstAssertionArgument(node) { + ct := c.getContextualType(node, ContextFlagsNone) + if contextualTypeOut != nil { + *contextualTypeOut = ct + *hasContextualTypeOut = true + } + if c.isConstTypeVariable(ct, 0) { + return true + } + } + if ast.IsParenthesizedExpression(parent) || ast.IsArrayLiteralExpression(parent) || ast.IsSpreadElement(parent) { + if hasContextualTypeOut != nil { + *hasContextualTypeOut = false + } + return c.isConstContext(parent) + } + if ast.IsPropertyAssignment(parent) || ast.IsShorthandPropertyAssignment(parent) || ast.IsTemplateSpan(parent) { + if hasContextualTypeOut != nil { + *hasContextualTypeOut = false + } + return c.isConstContext(parent.Parent) + } + return false } func (c *Checker) isInlineImportAttributes(node *ast.Node) bool { @@ -13919,11 +14152,37 @@ func (c *Checker) isConstTypeVariable(t *Type, depth int) bool { if depth >= 5 || t == nil { return false } + if t.flags&(TypeFlagsTypeParameter|TypeFlagsUnionOrIntersection|TypeFlagsIndexedAccess|TypeFlagsConditional|TypeFlagsSubstitution|TypeFlagsObject) == 0 { + return false + } + if t.flags&TypeFlagsObjectFlagsType != 0 { + if t.objectFlags&ObjectFlagsPrimitiveUnion != 0 { + return false + } + if t.objectFlags&(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables) == ObjectFlagsCouldContainTypeVariablesComputed { + return false + } + if t.flags&TypeFlagsObject != 0 && t.objectFlags&(ObjectFlagsMapped|ObjectFlagsReference) == 0 { + return false + } + } switch { case t.flags&TypeFlagsTypeParameter != 0: - return t.symbol != nil && core.Some(t.symbol.Declarations(), func(d *ast.Node) bool { return ast.HasSyntacticModifier(d, ast.ModifierFlagsConst) }) + if t.symbol != nil { + for _, d := range t.symbol.Declarations() { + if ast.HasSyntacticModifier(d, ast.ModifierFlagsConst) { + return true + } + } + } + return false case t.flags&TypeFlagsUnionOrIntersection != 0: - return core.Some(t.Types(), func(s *Type) bool { return c.isConstTypeVariable(s, depth) }) + for _, s := range t.Types() { + if c.isConstTypeVariable(s, depth+1) { + return true + } + } + return false case t.flags&TypeFlagsIndexedAccess != 0: return c.isConstTypeVariable(t.AsIndexedAccessType().objectType, depth+1) case t.flags&TypeFlagsConditional != 0: @@ -14086,7 +14345,7 @@ func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node if context != nil { mapper = context.nonFixingMapper } - restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSymbol(contextualSignature.parameters[0]), mapper)) + restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSignatureParameterSymbol(contextualSignature, 0), mapper)) if restType.flags&TypeFlagsUnion != 0 && everyType(restType, isTupleType) && !core.Some(fn.Parameters(), c.isSomeSymbolAssigned) { narrowedType := c.getFlowTypeOfReferenceEx(fn, restType, restType, nil /*flowContainer*/, getFlowNodeOfNode(location)) index := slices.Index(fn.Parameters(), declaration) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0) @@ -14150,14 +14409,18 @@ func (c *Checker) checkObjectLiteralMethod(node *ast.Node, checkMode CheckMode) func (c *Checker) checkExpressionForMutableLocation(node *ast.Node, checkMode CheckMode) *Type { t := c.checkExpressionEx(node, checkMode) - switch { - case c.isConstContext(node): + var contextualType *Type + var hasContextualType bool + if c.isConstContextEx(node, &contextualType, &hasContextualType) { return c.getRegularTypeOfLiteralType(t) - case isTypeAssertion(node): + } + if isTypeAssertion(node) { return t - default: - return c.getWidenedLiteralLikeTypeForContextualType(t, c.instantiateContextualType(c.getContextualType(node, ContextFlagsNone), node, ContextFlagsNone)) } + if !hasContextualType { + contextualType = c.getContextualType(node, ContextFlagsNone) + } + return c.getWidenedLiteralLikeTypeForContextualType(t, c.instantiateContextualType(contextualType, node, ContextFlagsNone)) } func (c *Checker) getResolvedSymbol(node *ast.Node) *ast.Symbol { @@ -14174,11 +14437,14 @@ func (c *Checker) getResolvedSymbol(node *ast.Node) *ast.Symbol { } func (c *Checker) getResolvedSymbolOrNil(node *ast.Node) *ast.Symbol { - return c.symbolNodeLinks.Get(node).resolvedSymbol + if links := c.symbolNodeLinks.TryGet(node); links != nil { + return links.resolvedSymbol + } + return nil } func (c *Checker) getReferencedValueOrAliasSymbol(reference *ast.Node) *ast.Symbol { - resolvedSymbol := c.symbolNodeLinks.Get(reference).resolvedSymbol + resolvedSymbol := c.getResolvedSymbolOrNil(reference) if resolvedSymbol != nil && resolvedSymbol != c.unknownSymbol { return resolvedSymbol } @@ -14246,6 +14512,7 @@ func (c *Checker) GetGlobalDiagnostics() []*ast.Diagnostic { } func (c *Checker) addDeferredDiagnostic(callback func()) { + c.diagnosticCount++ c.deferredDiagnosticCallbacks = append(c.deferredDiagnosticCallbacks, callback) } @@ -14257,6 +14524,7 @@ func (c *Checker) produceDeferredDiagnostics() { } func (c *Checker) addDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { + c.diagnosticCount++ // Discard diagnostics created while at the maximum number of recursive TypeToString invocations. if c.serializationLevel < maxSerializationLevel { return c.diagnostics.Add(diagnostic) @@ -14265,6 +14533,7 @@ func (c *Checker) addDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { } func (c *Checker) addSuggestionDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { + c.diagnosticCount++ // Discard diagnostics created while at the maximum number of recursive TypeToString invocations. if c.serializationLevel < maxSerializationLevel { return c.suggestionDiagnostics.Add(diagnostic) @@ -14375,21 +14644,21 @@ func (c *Checker) newProperty(name string, t *Type) *ast.Symbol { } func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTable) ast.SymbolTable { - if len(first) == 0 { + if first.Len() == 0 { return second } - if len(second) == 0 { + if second.Len() == 0 { return first } - combined := make(ast.SymbolTable) + combined := ast.NewSymbolTable() c.mergeSymbolTable(combined, first, false, nil) c.mergeSymbolTable(combined, second, false, nil) return combined } func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { - for id, sourceSymbol := range source { - targetSymbol := target[id] + for id, sourceSymbol := range source.All() { + targetSymbol := target.Get(id) var merged *ast.Symbol if targetSymbol != nil { merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) @@ -14416,7 +14685,7 @@ func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTabl merged.SetParent(mergedParent) } } - target[id] = merged + target.Set(id, merged) } } @@ -14621,16 +14890,15 @@ func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol { result.SetDeclarations(symbol.Declarations()[0:len(symbol.Declarations()):len(symbol.Declarations())]) result.SetParent(symbol.Parent()) result.SetValueDeclaration(symbol.ValueDeclaration()) - result.SetMembers(maps.Clone(symbol.Members()), &c.symbolExtraArena) - result.SetExports(maps.Clone(symbol.Exports()), &c.symbolExtraArena) + result.SetMembers(symbol.Members().Clone(), &c.symbolExtraArena) + result.SetExports(symbol.Exports().Clone(), &c.symbolExtraArena) c.recordMergedSymbol(result, symbol) return result } func (c *Checker) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - merged := c.mergedSymbols[symbol] - if merged != nil { + if symbol != nil && symbol.IsMerged() { + if merged := c.mergedSymbols[symbol]; merged != nil { return merged } } @@ -14645,6 +14913,7 @@ func (c *Checker) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { } func (c *Checker) recordMergedSymbol(target *ast.Symbol, source *ast.Symbol) { + source.MarkMerged() c.mergedSymbols[source] = target } @@ -14669,7 +14938,10 @@ func (c *Checker) getExportSymbolOfValueSymbolIfExported(symbol *ast.Symbol) *as func (c *Checker) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { symbol := node.Symbol() if symbol != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(symbol)) + if symbol.Flags()&ast.SymbolFlagsClassMember != 0 && symbol.Name() == ast.InternalSymbolNameComputed { + symbol = c.getLateBoundSymbolSlow(symbol) + } + return c.getMergedSymbol(symbol) } return nil } @@ -14679,7 +14951,11 @@ func (c *Checker) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { func (c *Checker) getSymbolOfNode(node *ast.Node) *ast.Symbol { data := node.DeclarationData() if data != nil && data.Symbol != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(data.Symbol)) + symbol := data.Symbol + if symbol.Flags()&ast.SymbolFlagsClassMember != 0 && symbol.Name() == ast.InternalSymbolNameComputed { + symbol = c.getLateBoundSymbolSlow(symbol) + } + return c.getMergedSymbol(symbol) } return nil } @@ -14688,6 +14964,10 @@ func (c *Checker) getLateBoundSymbol(symbol *ast.Symbol) *ast.Symbol { if symbol.Flags()&ast.SymbolFlagsClassMember == 0 || symbol.Name() != ast.InternalSymbolNameComputed { return symbol } + return c.getLateBoundSymbolSlow(symbol) +} + +func (c *Checker) getLateBoundSymbolSlow(symbol *ast.Symbol) *ast.Symbol { links := c.lateBoundLinks.Get(symbol) if links.lateSymbol == nil && core.Some(symbol.Declarations(), c.hasLateBindableName) { // force late binding of members/exports. This will set the late-bound symbol @@ -14888,13 +15168,13 @@ func (c *Checker) getTargetOfModuleDefault(moduleSymbol *ast.Symbol, node *ast.N } func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Node) { - if moduleSymbol.Exports() != nil && moduleSymbol.Exports()[node.Symbol().Name()] != nil { + if moduleSymbol.Exports() != nil && moduleSymbol.Exports().Get(node.Symbol().Name()) != nil { c.error(node, diagnostics.Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, c.symbolToString(moduleSymbol), c.symbolToString(node.Symbol())) } else { diagnostic := c.error(node.Name(), diagnostics.Module_0_has_no_default_export, c.symbolToString(moduleSymbol)) var exportStar *ast.Symbol if moduleSymbol.Exports() != nil { - exportStar = moduleSymbol.Exports()[ast.InternalSymbolNameExportStar] + exportStar = moduleSymbol.Exports().Get(ast.InternalSymbolNameExportStar) } if exportStar != nil { defaultExport := core.Find(exportStar.Declarations(), func(decl *ast.Declaration) bool { @@ -14902,7 +15182,7 @@ func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Nod return false } resolvedExternalModuleName := c.resolveExternalModuleName(decl, decl.ModuleSpecifier(), false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(decl))) - return resolvedExternalModuleName != nil && resolvedExternalModuleName.Exports()[ast.InternalSymbolNameDefault] != nil + return resolvedExternalModuleName != nil && resolvedExternalModuleName.Exports().Get(ast.InternalSymbolNameDefault) != nil }) if defaultExport != nil { diagnostic.AddRelatedInfo(createDiagnosticForNode(defaultExport, diagnostics.X_export_Asterisk_does_not_re_export_a_default)) @@ -14912,31 +15192,31 @@ func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Nod } func (c *Checker) resolveExportByName(moduleSymbol *ast.Symbol, name string, sourceNode *ast.Node, dontResolveAlias bool) *ast.Symbol { - exportValue := moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals] + exportValue := moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) var exportSymbol *ast.Symbol if exportValue != nil { exportSymbol = c.getPropertyOfTypeEx(c.getTypeOfSymbol(exportValue), name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) } else { - exportSymbol = moduleSymbol.Exports()[name] + exportSymbol = moduleSymbol.Exports().Get(name) } resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias) c.markSymbolOfAliasDeclarationIfTypeOnly(sourceNode, nil) return resolved } -func (c *Checker) getTargetOfNamespaceImport(node *ast.Node) *ast.Symbol { +func (c *Checker) getTargetOfNamespaceImport(node *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { moduleSpecifier := c.getModuleSpecifierForImportOrExport(node) immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node.Parent.Parent))) - resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier) + resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier, dontRecursivelyResolve) c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil) return resolved } -func (c *Checker) getTargetOfNamespaceExport(node *ast.Node) *ast.Symbol { +func (c *Checker) getTargetOfNamespaceExport(node *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { moduleSpecifier := c.getModuleSpecifierForImportOrExport(node) if moduleSpecifier != nil { immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node.Parent))) - resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier) + resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier, dontRecursivelyResolve) c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil) return resolved } @@ -14986,7 +15266,7 @@ func (c *Checker) getExternalModuleMember(node *ast.Node, specifier *ast.Node, d return nil } nameText := name.Text() - targetSymbol := c.resolveESModuleSymbol(moduleSymbol, specifier, moduleSpecifier) + targetSymbol := c.resolveESModuleSymbol(moduleSymbol, specifier, moduleSpecifier, false /*dontRecursivelyResolve*/) if targetSymbol != nil { // Note: The empty string is a valid module export name: // @@ -14999,7 +15279,7 @@ func (c *Checker) getExternalModuleMember(node *ast.Node, specifier *ast.Node, d } var symbolFromVariable *ast.Symbol // First check if module was specified with "export=". If so, get the member from the resolved type - if moduleSymbol != nil && moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals] != nil { + if moduleSymbol != nil && moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) != nil { symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(targetSymbol), nameText, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) } else { symbolFromVariable = c.getPropertyOfVariable(targetSymbol, nameText) @@ -15007,7 +15287,7 @@ func (c *Checker) getExternalModuleMember(node *ast.Node, specifier *ast.Node, d // if symbolFromVariable is export - get its final target symbolFromVariable = c.resolveSymbolEx(symbolFromVariable, dontResolveAlias) exportContainer := targetSymbol - if moduleSymbol != nil && moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals] != nil { + if moduleSymbol != nil && moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) != nil { // For `export =` modules, supplemental type/namespace exports live on the original module symbol. exportContainer = moduleSymbol } @@ -15085,14 +15365,14 @@ func (c *Checker) combineValueAndTypeSymbols(valueSymbol *ast.Symbol, typeSymbol result.SetParent(typeSymbol.Parent()) } result.SetValueDeclaration(valueSymbol.ValueDeclaration()) - result.SetMembers(maps.Clone(typeSymbol.Members()), &c.symbolExtraArena) - result.SetExports(maps.Clone(valueSymbol.Exports()), &c.symbolExtraArena) + result.SetMembers(typeSymbol.Members().Clone(), &c.symbolExtraArena) + result.SetExports(valueSymbol.Exports().Clone(), &c.symbolExtraArena) return result } func (c *Checker) getExportOfModule(symbol *ast.Symbol, nameText string, specifier *ast.Node, dontResolveAlias bool) *ast.Symbol { if symbol.Flags()&ast.SymbolFlagsModule != 0 { - exportSymbol := c.getExportsOfSymbol(symbol)[nameText] + exportSymbol := c.getExportsOfSymbol(symbol).Get(nameText) resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias) exportStarDeclaration := c.moduleSymbolLinks.Get(symbol).typeOnlyExportStarMap[nameText] c.markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportStarDeclaration) @@ -15202,7 +15482,7 @@ func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymb diagnostic.AddRelatedInfo(createDiagnosticForNode(suggestion.ValueDeclaration(), diagnostics.X_0_is_declared_here, suggestionName)) } } else { - if moduleSymbol.Exports()[ast.InternalSymbolNameDefault] != nil { + if moduleSymbol.Exports().Get(ast.InternalSymbolNameDefault) != nil { c.error(name, diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, moduleName, declarationName) } else { c.reportNonExportedMember(name, declarationName, moduleSymbol, moduleName) @@ -15213,20 +15493,24 @@ func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymb func (c *Checker) reportNonExportedMember(name *ast.Node, declarationName string, moduleSymbol *ast.Symbol, moduleName string) { var localSymbol *ast.Symbol if locals := moduleSymbol.ValueDeclaration().Locals(); locals != nil { - localSymbol = locals[name.Text()] + localSymbol = locals.Get(name.Text()) } exports := moduleSymbol.Exports() if localSymbol != nil { - if exportedEqualsSymbol := exports[ast.InternalSymbolNameExportEquals]; exportedEqualsSymbol != nil { + if exportedEqualsSymbol := exports.Get(ast.InternalSymbolNameExportEquals); exportedEqualsSymbol != nil { if c.getSymbolIfSameReference(exportedEqualsSymbol, localSymbol) != nil { c.reportInvalidImportEqualsExportMember(name, declarationName, moduleName) } else { c.error(name, diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName) } } else { - exportedSymbol := findInMap(exports, func(symbol *ast.Symbol) bool { - return c.getSymbolIfSameReference(symbol, localSymbol) != nil - }) + var exportedSymbol *ast.Symbol + for _, symbol := range exports.All() { + if c.getSymbolIfSameReference(symbol, localSymbol) != nil { + exportedSymbol = symbol + break + } + } var diagnostic *ast.Diagnostic if exportedSymbol != nil { diagnostic = c.error(name, diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, c.symbolToString(exportedSymbol)) @@ -15700,9 +15984,11 @@ func (c *Checker) resolveExternalModule( } return c.tryResolvePatternAmbientModule(c.getMergedSymbol(sourceFile.Symbol), moduleReference, importAttributesType) } - patternAmbientModule := c.tryResolvePatternAmbientModule(nil /*resolvedSymbol*/, moduleReference, importAttributesType) - if patternAmbientModule != nil { - return patternAmbientModule + if !c.isEmptyObjectType(importAttributesType) { + patternAmbientModule := c.tryResolvePatternAmbientModule(nil /*resolvedSymbol*/, moduleReference, importAttributesType) + if patternAmbientModule != nil { + return patternAmbientModule + } } if errorNode != nil && moduleNotFoundError != nil && !isSideEffectImport(errorNode) { c.error(errorNode, diagnostics.File_0_is_not_a_module, resolvedModule.ResolvedFileName) @@ -15794,8 +16080,8 @@ func (c *Checker) tryResolvePatternAmbientModule(resolvedSymbol *ast.Symbol, mod }) if len(candidates) > 0 { - augmentation := c.patternAmbientModuleAugmentations[moduleReference] - augmentationTarget := c.patternAmbientModuleAugmentationTargets[moduleReference] + augmentation := c.patternAmbientModuleAugmentations.Get(moduleReference) + augmentationTarget := c.patternAmbientModuleAugmentationTargets.Get(moduleReference) if len(candidates) == 1 { mergedCandidate := c.getMergedSymbol(candidates[0].Symbol) @@ -15949,7 +16235,7 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool func (c *Checker) GetAmbientModules() []*ast.Symbol { c.ambientModulesOnce.Do(func() { seen := make(map[*ast.Symbol]struct{}) - for sym, global := range c.globals { + for sym, global := range c.globals.All() { if ast.IsAmbientModuleSymbolName(sym) { c.ambientModules = append(c.ambientModules, global) seen[global] = struct{}{} @@ -15968,7 +16254,7 @@ func (c *Checker) GetAmbientModules() []*ast.Symbol { func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol { if moduleSymbol != nil { - exportEquals := c.resolveSymbolEx(moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals], dontResolveAlias) + exportEquals := c.resolveSymbolEx(moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals), dontResolveAlias) if exportEquals != nil { return c.getMergedSymbol(exportEquals) } @@ -15978,8 +16264,11 @@ func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontReso // Resolves the given external module symbol, possibly removing call and construct signatures or creating a // wrapper module with a synthetic default. -func (c *Checker) resolveESModuleSymbol(moduleSymbol *ast.Symbol, node *ast.Node, moduleSpecifier *ast.Node) *ast.Symbol { +func (c *Checker) resolveESModuleSymbol(moduleSymbol *ast.Symbol, node *ast.Node, moduleSpecifier *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { symbol := c.resolveExternalModuleSymbol(moduleSymbol, true /*dontResolveAlias*/) + if dontRecursivelyResolve { + return symbol + } if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) { // When the module has an export= with a pure alias, we transitively resolve and propagate any typeOnlyDeclaration symbol = c.getMergedSymbol(c.resolveIndirectionAlias(c.getSymbolOfDeclaration(node), symbol)) @@ -16118,12 +16407,12 @@ func (c *Checker) isCommonJSRequire(node *ast.Node) bool { } func (c *Checker) createDefaultPropertyWrapperForModule(symbol *ast.Symbol, originalSymbol *ast.Symbol, anonymousSymbol *ast.Symbol) *Type { - memberTable := make(ast.SymbolTable) + memberTable := ast.NewSymbolTable() newSymbol := c.newSymbol(ast.SymbolFlagsAlias, ast.InternalSymbolNameDefault) newSymbol.SetParent(originalSymbol) c.valueSymbolLinks.Get(newSymbol).setNameType(c, c.getStringLiteralType("default")) c.aliasSymbolLinks.Get(newSymbol).aliasTarget = c.resolveSymbol(symbol) - memberTable[ast.InternalSymbolNameDefault] = newSymbol + memberTable.Set(ast.InternalSymbolNameDefault, newSymbol) if anonymousSymbol == nil && originalSymbol != nil { anonymousSymbol = c.newSymbol(ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameObject) anonymousSymbol.SetDeclarations(originalSymbol.Declarations()) @@ -16135,8 +16424,8 @@ func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, re result := c.newSymbol(symbol.Flags(), symbol.Name()) result.SetDeclarations(slices.Clone(symbol.Declarations())) result.SetValueDeclaration(symbol.ValueDeclaration()) - result.SetMembers(maps.Clone(symbol.Members()), &c.symbolExtraArena) - result.SetExports(maps.Clone(symbol.Exports()), &c.symbolExtraArena) + result.SetMembers(symbol.Members().Clone(), &c.symbolExtraArena) + result.SetExports(symbol.Exports().Clone(), &c.symbolExtraArena) result.SetParent(symbol.Parent()) links := c.exportTypeLinks.Get(result) links.target = symbol @@ -16146,7 +16435,7 @@ func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, re return result } -func (c *Checker) getTargetOfAliasDeclaration(node *ast.Node) *ast.Symbol { +func (c *Checker) getTargetOfAliasDeclaration(node *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { if node == nil { return nil } @@ -16156,9 +16445,9 @@ func (c *Checker) getTargetOfAliasDeclaration(node *ast.Node) *ast.Symbol { case ast.KindImportClause: return c.getTargetOfImportClause(node) case ast.KindNamespaceImport: - return c.getTargetOfNamespaceImport(node) + return c.getTargetOfNamespaceImport(node, dontRecursivelyResolve) case ast.KindNamespaceExport: - return c.getTargetOfNamespaceExport(node) + return c.getTargetOfNamespaceExport(node, dontRecursivelyResolve) case ast.KindImportSpecifier, ast.KindBindingElement: return c.getTargetOfImportSpecifier(node) case ast.KindExportSpecifier: @@ -16320,7 +16609,7 @@ func (c *Checker) tryGetQualifiedNameAsValue(node *ast.Node) *ast.Symbol { } func (c *Checker) getSuggestedSymbolForNonexistentModule(name *ast.Node, targetModule *ast.Symbol) *ast.Symbol { - return c.getSpellingSuggestionForName(name.Text(), maps.Values(c.getExportsOfModule(targetModule)), ast.SymbolFlagsModuleMember) + return c.getSpellingSuggestionForName(name.Text(), c.getExportsOfModule(targetModule).Values(), ast.SymbolFlagsModuleMember) } func (c *Checker) getFullyQualifiedName(symbol *ast.Symbol, containingLocation *ast.Node) string { @@ -16360,12 +16649,12 @@ func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolu switch { case c.hasLateBindableName(member): if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) + lateSymbols = ast.NewSymbolTable() } c.lateBindMember(symbol, earlySymbols, lateSymbols, member) case c.hasLateBindableIndexSignature(member): if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) + lateSymbols = ast.NewSymbolTable() } c.lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member.AsNode() /* as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration */) } @@ -16373,11 +16662,11 @@ func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolu } } if isStatic { - if assignmentSymbol := symbol.Exports()[ast.InternalSymbolNameAssignmentDeclaration]; assignmentSymbol != nil { + if assignmentSymbol := symbol.Exports().Get(ast.InternalSymbolNameAssignmentDeclaration); assignmentSymbol != nil { for _, member := range assignmentSymbol.Declarations() { if c.hasLateBindableName(member) { if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) + lateSymbols = ast.NewSymbolTable() } c.lateBindMember(symbol, earlySymbols, lateSymbols, member) } @@ -16438,13 +16727,13 @@ func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTabl memberName := getPropertyNameFromType(t) symbolFlags := decl.Symbol().Flags() // Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations. - lateSymbol := lateSymbols[memberName] + lateSymbol := lateSymbols.Get(memberName) if lateSymbol == nil { lateSymbol = c.newSymbolEx(ast.SymbolFlagsNone, memberName, ast.CheckFlagsLate) - lateSymbols[memberName] = lateSymbol + lateSymbols.Set(memberName, lateSymbol) } // Report an error if there's a symbol declaration with the same name and conflicting flags. - earlySymbol := earlySymbols[memberName] + earlySymbol := earlySymbols.Get(memberName) if lateSymbol.Flags()&getExcludedSymbolFlags(symbolFlags) != 0 { // If we have an existing early-bound member, combine its declarations so that we can // report an error at each declaration. @@ -16480,16 +16769,16 @@ func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTabl func (c *Checker) lateBindIndexSignature(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) { // First, late bind the index symbol itself, if needed - indexSymbol := lateSymbols[ast.InternalSymbolNameIndex] + indexSymbol := lateSymbols.Get(ast.InternalSymbolNameIndex) if indexSymbol == nil { - early := earlySymbols[ast.InternalSymbolNameIndex] + early := earlySymbols.Get(ast.InternalSymbolNameIndex) if early == nil { indexSymbol = c.newSymbolEx(ast.SymbolFlagsNone, ast.InternalSymbolNameIndex, ast.CheckFlagsLate) } else { indexSymbol = c.cloneSymbol(early) indexSymbol.SetCheckFlags(indexSymbol.CheckFlags() | ast.CheckFlagsLate) } - lateSymbols[ast.InternalSymbolNameIndex] = indexSymbol + lateSymbols.Set(ast.InternalSymbolNameIndex, indexSymbol) } // Then just add the computed name as a late bound declaration // (note: unlike `addDeclarationToLateBoundSymbol` we do not set up a `.lateSymbol` on `decl`'s links, @@ -16560,50 +16849,87 @@ type ExportCollisionTable = map[string]*ExportCollision func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports ast.SymbolTable, typeOnlyExportStarMap map[string]*ast.Node) { var visitedSymbols []*ast.Symbol - nonTypeOnlyNames := collections.NewSetWithSizeHint[string](len(moduleSymbol.Exports())) + var nonTypeOnlySymbols []*ast.Symbol // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. + var visit func(*ast.Symbol, *ast.Node, bool) ast.SymbolTable visit = func(symbol *ast.Symbol, exportStar *ast.Node, isTypeOnly bool) ast.SymbolTable { if !isTypeOnly && symbol != nil { - // Add non-type-only names before checking if we've visited this module, + // Add non-type-only symbols before checking if we've visited this module, // because we might have visited it via an 'export type *', and visiting // again with 'export *' will override the type-onlyness of its exports. - for name := range symbol.Exports() { - nonTypeOnlyNames.Add(name) - } + nonTypeOnlySymbols = append(nonTypeOnlySymbols, symbol) } if symbol == nil || symbol.Exports() == nil || slices.Contains(visitedSymbols, symbol) { return nil } visitedSymbols = append(visitedSymbols, symbol) - symbols := maps.Clone(symbol.Exports()) - // All export * declarations are collected in an __export symbol by the binder - exportStars := symbol.Exports()[ast.InternalSymbolNameExportStar] + exportStars := symbol.Exports().Get(ast.InternalSymbolNameExportStar) + var symbols ast.SymbolTable if exportStars != nil { - nestedSymbols := make(ast.SymbolTable) - lookupTable := make(ExportCollisionTable) - for _, node := range exportStars.Declarations() { + symbols = symbol.Exports().Clone() + decls := exportStars.Declarations() + if len(decls) == 1 { + node := decls[0] resolvedModule := c.resolveExternalModuleName(node, node.ModuleSpecifier(), false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node))) exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.IsTypeOnly()) - c.extendExportSymbols(nestedSymbols, exportedSymbols, lookupTable, node) - } - for id, s := range lookupTable { - // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if id == ast.InternalSymbolNameExportEquals || len(s.exportsWithDuplicate) == 0 || symbols[id] != nil { - continue + c.extendExportSymbols(symbols, exportedSymbols, nil, nil) + } else if len(decls) > 1 { + nestedSymbols := c.acquireSymbolTable() + var lookupTable ExportCollisionTable + exportedByStar := make([]ast.SymbolTable, len(decls)) + for i, node := range decls { + resolvedModule := c.resolveExternalModuleName(node, node.ModuleSpecifier(), false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node))) + exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.IsTypeOnly()) + exportedByStar[i] = exportedSymbols + for id, sourceSymbol := range exportedSymbols.All() { + if id == ast.InternalSymbolNameDefault { + continue + } + targetSymbol := nestedSymbols.Get(id) + if targetSymbol == nil { + nestedSymbols.Set(id, sourceSymbol) + } else if c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { + if lookupTable == nil { + lookupTable = make(ExportCollisionTable) + } + s := lookupTable[id] + if s == nil { + var specText string + for j := range i { + if exportedByStar[j].Get(id) != nil { + specText = scanner.GetTextOfNode(decls[j].ModuleSpecifier()) + break + } + } + s = &ExportCollision{specifierText: specText} + lookupTable[id] = s + } + s.exportsWithDuplicate = append(s.exportsWithDuplicate, node) + } + } } - for _, node := range s.exportsWithDuplicate { - c.addDiagnostic(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, s.specifierText, id)) + for id, s := range lookupTable { + // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself + if id == ast.InternalSymbolNameExportEquals || len(s.exportsWithDuplicate) == 0 || symbols.Get(id) != nil { + continue + } + for _, node := range s.exportsWithDuplicate { + c.addDiagnostic(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, s.specifierText, id)) + } } + c.extendExportSymbols(symbols, nestedSymbols, nil, nil) + c.releaseSymbolTable(nestedSymbols) } - c.extendExportSymbols(symbols, nestedSymbols, nil, nil) + } else { + symbols = symbol.Exports() } if exportStar != nil && exportStar.IsTypeOnly() { if typeOnlyExportStarMap == nil { typeOnlyExportStarMap = make(map[string]*ast.Node) } - for name := range symbols { + for name := range symbols.All() { typeOnlyExportStarMap[name] = exportStar } } @@ -16611,7 +16937,7 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as } var originalModule *ast.Symbol if moduleSymbol != nil { - if c.resolveSymbolEx(moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals], false /*dontResolveAlias*/) != nil { + if c.resolveSymbolEx(moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals), false /*dontResolveAlias*/) != nil { originalModule = moduleSymbol } } @@ -16619,24 +16945,31 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as moduleSymbol = c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) exports = visit(moduleSymbol, nil, false) if exports == nil { - exports = make(ast.SymbolTable) + exports = ast.NewSymbolTable() } // A CommonJS module defined by an 'export=' might also export typedefs, stored on the original module - if originalModule != nil && len(originalModule.Exports()) > 1 { - for _, symbol := range originalModule.Exports() { + if originalModule != nil && originalModule.Exports().Len() > 1 { + var cloned bool + for _, symbol := range originalModule.Exports().All() { if symbol.Name() == ast.InternalSymbolNameExportEquals || symbol.Name() == ast.InternalSymbolNameExportStar { continue } flags := c.getSymbolFlags(symbol) - if flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 && - flags&ast.SymbolFlagsValue == 0 && - exports[symbol.Name()] == nil { - exports[symbol.Name()] = symbol + if flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 && flags&ast.SymbolFlagsValue == 0 && exports.Get(symbol.Name()) == nil { + if !cloned && moduleSymbol != nil && moduleSymbol.Exports() != nil && moduleSymbol.Exports().Get(ast.InternalSymbolNameExportStar) == nil { + exports = exports.Clone() + cloned = true + } + exports.Set(symbol.Name(), symbol) } } } - for name := range nonTypeOnlyNames.Keys() { - delete(typeOnlyExportStarMap, name) + if typeOnlyExportStarMap != nil { + for _, sym := range nonTypeOnlySymbols { + for name := range sym.Exports().All() { + delete(typeOnlyExportStarMap, name) + } + } } return exports, typeOnlyExportStarMap } @@ -16646,13 +16979,13 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ func (c *Checker) extendExportSymbols(target ast.SymbolTable, source ast.SymbolTable, lookupTable ExportCollisionTable, exportNode *ast.Node) { - for id, sourceSymbol := range source { + for id, sourceSymbol := range source.All() { if id == ast.InternalSymbolNameDefault { continue } - targetSymbol := target[id] + targetSymbol := target.Get(id) if targetSymbol == nil { - target[id] = sourceSymbol + target.Set(id, sourceSymbol) if lookupTable != nil && exportNode != nil { lookupTable[id] = &ExportCollision{ specifierText: scanner.GetTextOfNode(exportNode.ModuleSpecifier()), @@ -16689,7 +17022,7 @@ func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol { if node == nil { panic("Unexpected nil in resolveAlias for symbol: " + c.symbolToString(symbol)) } - target := c.getTargetOfAliasDeclaration(node) + target := c.getTargetOfAliasDeclaration(node, false /*dontRecursivelyResolve*/) if ast.IsNonLocalAlias(target, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) { // When the target is a pure alias, we transitively resolve and propagate any typeOnlyDeclaration target = c.resolveIndirectionAlias(symbol, target) @@ -16883,7 +17216,7 @@ func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.No } else { t = c.getTypeOfExpression(location) } - if c.getExportSymbolOfValueSymbolIfExported(c.symbolNodeLinks.Get(location).resolvedSymbol) == symbol { + if c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbolOrNil(location)) == symbol { return c.removeOptionalTypeMarker(t) } } @@ -16903,32 +17236,49 @@ func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.No return c.getNonMissingTypeOfSymbol(symbol) } +const ( + symbolFlagsWithCachedType = ast.SymbolFlagsVariable | ast.SymbolFlagsProperty | ast.SymbolFlagsFunction | ast.SymbolFlagsMethod | ast.SymbolFlagsClass | ast.SymbolFlagsEnum | ast.SymbolFlagsValueModule | ast.SymbolFlagsEnumMember | ast.SymbolFlagsAccessor | ast.SymbolFlagsAlias + checkFlagsWithCachedType = ast.CheckFlagsDeferredType | ast.CheckFlagsInstantiated | ast.CheckFlagsMapped | ast.CheckFlagsReverseMapped +) + func (c *Checker) getTypeOfSymbol(symbol *ast.Symbol) *Type { - if symbol.CheckFlags()&ast.CheckFlagsDeferredType != 0 { + if symbol.Flags()&symbolFlagsWithCachedType != 0 || symbol.CheckFlags()&checkFlagsWithCachedType != 0 { + if links := c.valueSymbolLinks.Get(symbol); links.resolvedType != nil { + return links.resolvedType + } + return c.getTypeOfSymbolSlow(symbol) + } + return c.errorType +} + +func (c *Checker) getTypeOfSymbolSlow(symbol *ast.Symbol) *Type { + checkFlags := symbol.CheckFlags() + if checkFlags&ast.CheckFlagsDeferredType != 0 { return c.getTypeOfSymbolWithDeferredType(symbol) } - if symbol.CheckFlags()&ast.CheckFlagsInstantiated != 0 { + if checkFlags&ast.CheckFlagsInstantiated != 0 { return c.getTypeOfInstantiatedSymbol(symbol) } - if symbol.CheckFlags()&ast.CheckFlagsMapped != 0 { + if checkFlags&ast.CheckFlagsMapped != 0 { return c.getTypeOfMappedSymbol(symbol) } - if symbol.CheckFlags()&ast.CheckFlagsReverseMapped != 0 { + if checkFlags&ast.CheckFlagsReverseMapped != 0 { return c.getTypeOfReverseMappedSymbol(symbol) } - if symbol.Flags()&ast.SymbolFlagsAccessor != 0 { + flags := symbol.Flags() + if flags&ast.SymbolFlagsAccessor != 0 { return c.getTypeOfAccessors(symbol) } - if symbol.Flags()&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 { + if flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 { return c.getTypeOfVariableOrParameterOrProperty(symbol) } - if symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 { + if flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 { return c.getTypeOfFuncClassEnumModule(symbol) } - if symbol.Flags()&ast.SymbolFlagsEnumMember != 0 { + if flags&ast.SymbolFlagsEnumMember != 0 { return c.getTypeOfEnumMember(symbol) } - if symbol.Flags()&ast.SymbolFlagsAlias != 0 { + if flags&ast.SymbolFlagsAlias != 0 { return c.getTypeOfAlias(symbol) } return c.errorType @@ -17247,14 +17597,14 @@ func (c *Checker) padObjectLiteralType(t *Type, pattern *ast.Node) *Type { if len(missingElements) == 0 { return t } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for _, prop := range c.getPropertiesOfObjectType(t) { - members[prop.Name()] = prop + members.Set(prop.Name(), prop) } for _, e := range missingElements { symbol := c.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsOptional, c.getPropertyNameFromBindingElement(e)) c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, false /*includePatternInType*/, true /*reportErrors*/) - members[symbol.Name()] = symbol + members.Set(symbol.Name(), symbol) } result := c.newAnonymousType(t.symbol, members, nil, nil, c.getIndexInfosOfType(t)) result.objectFlags = t.objectFlags @@ -17442,7 +17792,7 @@ func (c *Checker) isMixinConstructorType(t *Type) bool { if len(signatures) == 1 { s := signatures[0] if len(s.TypeParameters()) == 0 && len(s.parameters) == 1 && signatureHasRestParameter(s) { - paramType := c.getTypeOfParameter(s.parameters[0]) + paramType := c.getTypeOfSignatureParameter(s, 0) return IsTypeAny(paramType) || c.getElementTypeOfArrayType(paramType) == c.anyType } } @@ -17458,6 +17808,95 @@ func (c *Checker) getTypeOfParameter(symbol *ast.Symbol) *Type { return c.addOptionalityEx(c.getTypeOfSymbol(symbol), false, declaration != nil && (declaration.Initializer() != nil || isOptionalDeclaration(declaration))) } +func (c *Checker) getTypeOfSignatureParameter(signature *Signature, pos int) *Type { + declaration := signature.parameters[pos].ValueDeclaration() + return c.addOptionalityEx(c.getTypeOfSignatureParameterSymbol(signature, pos), false, declaration != nil && (declaration.Initializer() != nil || isOptionalDeclaration(declaration))) +} + +func (c *Checker) getTypeOfSignatureParameterSymbol(signature *Signature, pos int) *Type { + if signature.flags&signatureFlagsUninstantiatedParameters == 0 { + return c.getTypeOfSymbol(signature.parameters[pos]) + } + if pos == 0 { + if signature.firstParamType != nil { + return signature.firstParamType + } + } else if signature.extra != nil && pos-1 < len(signature.extra.paramTypes) { + if t := signature.extra.paramTypes[pos-1]; t != nil { + return t + } + } + target := signature.target + mapper := signature.mapper + for target.flags&signatureFlagsUninstantiatedParameters != 0 { + mapper = c.combineTypeMappers(target.mapper, mapper) + target = target.target + } + param := target.parameters[pos] + if param.CheckFlags()&ast.CheckFlagsInstantiated != 0 { + links := c.valueSymbolLinks.Get(param) + param = links.target + mapper = c.combineTypeMappers(links.mapper, mapper) + } + t := c.getTypeOfSymbol(param) + if c.couldContainTypeVariables(t) || param.Flags()&ast.SymbolFlagsSetAccessor != 0 { + t = c.instantiateType(t, mapper) + } + if pos == 0 { + signature.firstParamType = t + } else { + extra := signature.ensureExtra(c) + if extra.paramTypes == nil { + extra.paramTypes = c.typeListArena.NewSlice(len(signature.parameters) - 1) + } + extra.paramTypes[pos-1] = t + } + return t +} + +func (c *Checker) getSignatureParameters(sig *Signature) []*ast.Symbol { + if sig.flags&signatureFlagsUninstantiatedParameters != 0 { + sig.flags &^= signatureFlagsUninstantiatedParameters + target := sig.target + mapper := sig.mapper + for target.flags&signatureFlagsUninstantiatedParameters != 0 { + mapper = c.combineTypeMappers(target.mapper, mapper) + target = target.target + } + sig.parameters = c.instantiateSymbols(target.parameters, mapper) + } + return sig.parameters +} + +func (c *Checker) getSignatureThisParameter(sig *Signature) *ast.Symbol { + if sig.flags&signatureFlagsUninstantiatedThisParameter != 0 { + sig.flags &^= signatureFlagsUninstantiatedThisParameter + target := sig.target + mapper := sig.mapper + for target.flags&signatureFlagsUninstantiatedThisParameter != 0 { + mapper = c.combineTypeMappers(target.mapper, mapper) + target = target.target + } + sig.ensureExtra(c).thisParameter = c.instantiateSymbol(target.ThisParameter(), mapper) + } + return sig.ThisParameter() +} + +func (c *Checker) resolveExportedSignature(sig *Signature) *Signature { + for s := sig; s != nil; s = s.target { + c.getSignatureParameters(s) + c.getSignatureThisParameter(s) + } + return sig +} + +func (c *Checker) resolveExportedSignatures(sigs []*Signature) []*Signature { + for _, sig := range sigs { + c.resolveExportedSignature(sig) + } + return sigs +} + func (c *Checker) getConstraintOfType(t *Type) *Type { switch { case t.flags&TypeFlagsTypeParameter != 0: @@ -17676,7 +18115,8 @@ func (c *Checker) getConstraintFromConditionalType(t *Type) *Type { func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedDefaultConstraint == nil { + ex := d.ensureExtra(c) + if ex.resolvedDefaultConstraint == nil { // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, @@ -17685,19 +18125,20 @@ func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type { falseConstraint := c.getFalseTypeFromConditionalType(t) switch { case IsTypeAny(trueConstraint): - d.resolvedDefaultConstraint = falseConstraint + ex.resolvedDefaultConstraint = falseConstraint case IsTypeAny(falseConstraint): - d.resolvedDefaultConstraint = trueConstraint + ex.resolvedDefaultConstraint = trueConstraint default: - d.resolvedDefaultConstraint = c.getUnionType([]*Type{trueConstraint, falseConstraint}) + ex.resolvedDefaultConstraint = c.getUnionType([]*Type{trueConstraint, falseConstraint}) } } - return d.resolvedDefaultConstraint + return ex.resolvedDefaultConstraint } func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedConstraintOfDistributive == nil { + ex := d.ensureExtra(c) + if ex.resolvedConstraintOfDistributive == nil { // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained // type parameter. If so, create an instantiation of the conditional type where T is replaced // with its constraint. We do this because if the constraint is a union type it will be distributed @@ -17706,7 +18147,7 @@ func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { // We skip returning a distributive constraint for a restrictive instantiation of a conditional type // as the constraint for all type params (check type included) have been replace with `unknown`, which // is going to produce even more false positive/negative results than the distribute constraint already does. - // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter + // Please note: the distributive constraint is a kludge for emulating what a negated type could do to filter // a union - once negated types exist and are applied to the conditional false branch, this "constraint" // likely doesn't need to exist. if d.root.isDistributive && c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}] != t { @@ -17717,15 +18158,15 @@ func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { if constraint != nil && constraint != d.checkType { instantiated := c.getConditionalTypeInstantiation(t, prependTypeMapping(d.root.checkType, constraint, d.mapper), true /*forConstraint*/, nil) if instantiated.flags&TypeFlagsNever == 0 { - d.resolvedConstraintOfDistributive = instantiated + ex.resolvedConstraintOfDistributive = instantiated return instantiated } } } - d.resolvedConstraintOfDistributive = c.noConstraintType + ex.resolvedConstraintOfDistributive = c.noConstraintType } - if d.resolvedConstraintOfDistributive != c.noConstraintType { - return d.resolvedConstraintOfDistributive + if ex.resolvedConstraintOfDistributive != c.noConstraintType { + return ex.resolvedConstraintOfDistributive } return nil } @@ -17752,8 +18193,6 @@ func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type { d.allTypeParameters = append(typeParameters, d.thisType) d.outerTypeParameterCount = len(outerTypeParameters) d.resolvedTypeArguments = d.TypeParameters() - d.instantiations = make(map[CacheHashKey]*Type) - d.instantiations[getTypeListKey(d.resolvedTypeArguments)] = t d.target = t } } @@ -17800,58 +18239,394 @@ type keyBuilder struct { inlineBuffer [192]byte } -func (b *keyBuilder) hash() CacheHashKey { - if b.overflowBuffer == nil { - return CacheHashKey(xxh3.Hash128(b.inlineBuffer[:b.inlineLength])) - } - return CacheHashKey(xxh3.Hash128(append(b.overflowBuffer, b.inlineBuffer[:b.inlineLength]...))) -} +var wasmKeyBuilderPool []*keyBuilder -// spill moves the buffered bytes onto the end of overflowBuffer, so the key's byte -// stream stays overflowBuffer followed by inlineBuffer. -func (b *keyBuilder) spill() { - b.overflowBuffer = append(b.overflowBuffer, b.inlineBuffer[:b.inlineLength]...) - b.inlineLength = 0 +func ResetWasmPools() { + wasmKeyBuilderPool = nil + primitiveTypeAliasSuggestions() } -func (b *keyBuilder) reserve(size int) { - if b.overflowBuffer != nil || b.inlineLength+size > len(b.inlineBuffer) { - b.overflowBuffer = slices.Grow(b.overflowBuffer, b.inlineLength+size) +func acquireKeyBuilder() *keyBuilder { + if n := len(wasmKeyBuilderPool); n > 0 { + b := wasmKeyBuilderPool[n-1] + wasmKeyBuilderPool = wasmKeyBuilderPool[:n-1] + b.inlineLength = 0 + if b.overflowBuffer != nil { + b.overflowBuffer = b.overflowBuffer[:0] + } + return b } + return &keyBuilder{} } -func (b *keyBuilder) writeByte(c byte) { - if b.inlineLength == len(b.inlineBuffer) { - b.spill() +func (b *keyBuilder) releaseHash() CacheHashKey { + h := b.hash() + if runtime.GOARCH == "wasm" { + wasmKeyBuilderPool = append(wasmKeyBuilderPool, b) } - b.inlineBuffer[b.inlineLength] = c - b.inlineLength++ + return h } -func (b *keyBuilder) writeString(s string) { - if b.inlineLength+len(s) > len(b.inlineBuffer) { - b.spill() - if len(s) > len(b.inlineBuffer) { - b.overflowBuffer = append(b.overflowBuffer, s...) - return - } - } - b.inlineLength += copy(b.inlineBuffer[b.inlineLength:], s) +type typeCacheSlot struct { + key CacheHashKey + val *Type } -func (b *keyBuilder) writeUint32(v uint32) { - if b.inlineLength+4 > len(b.inlineBuffer) { - b.spill() - } - binary.LittleEndian.PutUint32(b.inlineBuffer[b.inlineLength:], v) - b.inlineLength += 4 +type typeCacheTable struct { + first typeCacheSlot + slots []typeCacheSlot + count uint32 + mask uint32 } -func (b *keyBuilder) writeUint64(v uint64) { - if b.inlineLength+8 > len(b.inlineBuffer) { - b.spill() +func (t *typeCacheTable) get(key CacheHashKey) *Type { + if t == nil { + return nil } - binary.LittleEndian.PutUint64(b.inlineBuffer[b.inlineLength:], v) + if len(t.slots) == 0 { + if t.first.val != nil && t.first.key == key { + return t.first.val + } + return nil + } + mask := t.mask + idx := uint32(key.Lo^key.Hi) & mask + for { + s := &t.slots[idx] + if s.val == nil { + return nil + } + if s.key == key { + return s.val + } + idx = (idx + 1) & mask + } +} + +func (t *typeCacheTable) set(c *Checker, key CacheHashKey, val *Type) { + if len(t.slots) == 0 { + if c != nil { + t.slots = c.typeCacheSlotsArena.NewSlice(256) + } else { + t.slots = make([]typeCacheSlot, 256) + } + t.mask = 255 + } else if (t.count+1)*4 > uint32(len(t.slots))*3 { + t.grow(c) + } + mask := t.mask + idx := uint32(key.Lo^key.Hi) & mask + for { + s := &t.slots[idx] + if s.val == nil { + s.key = key + s.val = val + t.count++ + return + } + if s.key == key { + s.val = val + return + } + idx = (idx + 1) & mask + } +} + +func (t *ObjectType) setInstantiation(c *Checker, key CacheHashKey, val *Type) { + if t.instantiations == nil { + t.instantiations = c.typeCacheTableArena.New() + } + t.instantiations.setSmall(c, key, val) +} + +func (t *typeCacheTable) setSmall(c *Checker, key CacheHashKey, val *Type) { + if len(t.slots) == 0 { + if t.first.val == nil || t.first.key == key { + t.first.key = key + t.first.val = val + t.count = 1 + return + } + if c != nil && len(c.freeTypeCacheSlots[2]) > 0 { + n := len(c.freeTypeCacheSlots[2]) - 1 + t.slots = c.freeTypeCacheSlots[2][n] + c.freeTypeCacheSlots[2] = c.freeTypeCacheSlots[2][:n] + } else { + t.slots = c.typeCacheSlotsArena.NewSlice(4) + } + t.mask = 3 + firstIdx := uint32(t.first.key.Lo^t.first.key.Hi) & 3 + t.slots[firstIdx] = t.first + t.first = typeCacheSlot{} + } else if (t.count+1)*4 > uint32(len(t.slots))*3 { + t.grow(c) + } + mask := t.mask + idx := uint32(key.Lo^key.Hi) & mask + for { + s := &t.slots[idx] + if s.val == nil { + s.key = key + s.val = val + t.count++ + return + } + if s.key == key { + s.val = val + return + } + idx = (idx + 1) & mask + } +} + +func (t *typeCacheTable) grow(c *Checker) { + oldSlots := t.slots + newCap := uint32(len(oldSlots)) * 2 + var newSlots []typeCacheSlot + newShift := bits.TrailingZeros32(newCap) + if c != nil && newShift < len(c.freeTypeCacheSlots) && len(c.freeTypeCacheSlots[newShift]) > 0 { + n := len(c.freeTypeCacheSlots[newShift]) - 1 + newSlots = c.freeTypeCacheSlots[newShift][n] + c.freeTypeCacheSlots[newShift] = c.freeTypeCacheSlots[newShift][:n] + } else if c != nil && newCap <= 1024 { + newSlots = c.typeCacheSlotsArena.NewSlice(int(newCap)) + } else { + newSlots = make([]typeCacheSlot, newCap) + } + mask := newCap - 1 + for i := range oldSlots { + s := &oldSlots[i] + if s.val != nil { + idx := uint32(s.key.Lo^s.key.Hi) & mask + for newSlots[idx].val != nil { + idx = (idx + 1) & mask + } + newSlots[idx] = *s + s.val = nil + } + } + if c != nil { + oldShift := newShift - 1 + if oldShift < len(c.freeTypeCacheSlots) { + c.freeTypeCacheSlots[oldShift] = append(c.freeTypeCacheSlots[oldShift], oldSlots) + } + } + t.slots = newSlots + t.mask = mask +} + +type activeMapperSlot struct { + key CacheHashKey + val *Type + epoch uint32 +} + +type activeMapperCache struct { + slots []activeMapperSlot + count uint32 + mask uint32 + epoch uint32 +} + +func (t *activeMapperCache) get(key CacheHashKey) *Type { + slots := t.slots + if len(slots) == 0 || t.count == 0 { + return nil + } + epoch := t.epoch + mask := t.mask + idx := uint32(key.Lo^key.Hi) & mask + for { + s := &slots[idx] + if s.epoch != epoch { + return nil + } + if s.key == key { + return s.val + } + idx = (idx + 1) & mask + } +} + +func (t *activeMapperCache) set(key CacheHashKey, val *Type) { + if len(t.slots) == 0 { + t.slots = make([]activeMapperSlot, 16) + t.mask = 15 + t.epoch = 1 + } else if (t.count+1)*4 >= uint32(len(t.slots))*3 { + t.grow() + } + slots := t.slots + epoch := t.epoch + mask := t.mask + idx := uint32(key.Lo^key.Hi) & mask + for { + s := &slots[idx] + if s.epoch != epoch { + s.key = key + s.val = val + s.epoch = epoch + t.count++ + return + } + if s.key == key { + s.val = val + return + } + idx = (idx + 1) & mask + } +} + +func (t *activeMapperCache) grow() { + oldSlots := t.slots + oldEpoch := t.epoch + newCap := uint32(len(oldSlots)) * 2 + newSlots := make([]activeMapperSlot, newCap) + mask := newCap - 1 + for i := range oldSlots { + s := &oldSlots[i] + if s.epoch == oldEpoch { + idx := uint32(s.key.Lo^s.key.Hi) & mask + for newSlots[idx].epoch != 0 { + idx = (idx + 1) & mask + } + newSlots[idx] = activeMapperSlot{key: s.key, val: s.val, epoch: 1} + } + } + t.slots = newSlots + t.mask = mask + t.epoch = 1 +} + +func (t *activeMapperCache) clear() { + if t.count != 0 { + t.count = 0 + t.epoch++ + if t.epoch == 0 { + clear(t.slots) + t.epoch = 1 + } + } +} + +const ( + packedInstantiationKeyTag = 0x1a5e + packedConditionalKeyTag = 0xc0d7 + packedUnionKeyTag = 0x55aa + packedIntersectionKeyTag = 0x33cc + packedIndexedAccessKeyTag = 0x9966 + packedUnionFrontKeyTag = 0x70f0 +) + +func packedTypeListKey(tag uint64, types []*Type, flag bool) (CacheHashKey, bool) { + return packedTypeListKeyWithAlias(tag, types, nil, flag) +} + +func packedSingleTypeKey(tag uint64, t *Type, flag bool) CacheHashKey { + var flagBit uint64 + if flag { + flagBit = 1 + } + id0 := uint64(t.id) + hi := (tag << 48) | (flagBit << 34) | (1 << 32) + y := (id0 ^ (hi * 0x9e3779b97f4a7c15)) * 0xbf58476d1ce4e5b9 + return CacheHashKey{Hi: hi, Lo: y ^ (y >> 32)} +} + +func packedTypeListKeyWithAlias(tag uint64, types []*Type, alias *TypeAlias, flag bool) (CacheHashKey, bool) { + var flagBit uint64 + if flag { + flagBit = 1 + } + if alias == nil { + if len(types) > 3 { + return CacheHashKey{}, false + } + var id0, id1, id2 uint64 + if len(types) > 0 { + id0 = uint64(types[0].id) + if len(types) > 1 { + id1 = uint64(types[1].id) + if len(types) > 2 { + id2 = uint64(types[2].id) + } + } + } + hi := (tag << 48) | (flagBit << 34) | (uint64(len(types)) << 32) | id2 + y := ((id0 | (id1 << 32)) ^ (hi * 0x9e3779b97f4a7c15)) * 0xbf58476d1ce4e5b9 + return CacheHashKey{Hi: hi, Lo: y ^ (y >> 32)}, true + } + if len(types) <= 1 && len(alias.typeArguments) <= 1 { + var id0, aliasArgId uint64 + if len(types) == 1 { + id0 = uint64(types[0].id) + } + if len(alias.typeArguments) == 1 { + aliasArgId = uint64(alias.typeArguments[0].id) + } + aliasSymId := uint64(ast.GetSymbolId(alias.symbol)) + hi := (tag << 48) | (1 << 36) | (uint64(len(alias.typeArguments)) << 35) | (flagBit << 34) | (uint64(len(types)) << 32) | aliasSymId + y := ((id0 | (aliasArgId << 32)) ^ (hi * 0x9e3779b97f4a7c15)) * 0xbf58476d1ce4e5b9 + return CacheHashKey{Hi: hi, Lo: y ^ (y >> 32)}, true + } + return CacheHashKey{}, false +} + +func (b *keyBuilder) hash() CacheHashKey { + if b.overflowBuffer == nil { + return CacheHashKey(xxh3.Hash128(b.inlineBuffer[:b.inlineLength])) + } + if b.inlineLength > 0 { + b.spill() + } + return CacheHashKey(xxh3.Hash128(b.overflowBuffer)) +} + +// spill moves the buffered bytes onto the end of overflowBuffer, so the key's byte +// stream stays overflowBuffer followed by inlineBuffer. +func (b *keyBuilder) spill() { + b.overflowBuffer = append(b.overflowBuffer, b.inlineBuffer[:b.inlineLength]...) + b.inlineLength = 0 +} + +func (b *keyBuilder) reserve(size int) { + if b.overflowBuffer != nil || b.inlineLength+size > len(b.inlineBuffer) { + b.overflowBuffer = slices.Grow(b.overflowBuffer, b.inlineLength+size) + } +} + +func (b *keyBuilder) writeByte(c byte) { + if b.inlineLength == len(b.inlineBuffer) { + b.spill() + } + b.inlineBuffer[b.inlineLength] = c + b.inlineLength++ +} + +func (b *keyBuilder) writeString(s string) { + if b.inlineLength+len(s) > len(b.inlineBuffer) { + b.spill() + if len(s) > len(b.inlineBuffer) { + b.overflowBuffer = append(b.overflowBuffer, s...) + return + } + } + b.inlineLength += copy(b.inlineBuffer[b.inlineLength:], s) +} + +func (b *keyBuilder) writeUint32(v uint32) { + if b.inlineLength+4 > len(b.inlineBuffer) { + b.spill() + } + binary.LittleEndian.PutUint32(b.inlineBuffer[b.inlineLength:], v) + b.inlineLength += 4 +} + +func (b *keyBuilder) writeUint64(v uint64) { + if b.inlineLength+8 > len(b.inlineBuffer) { + b.spill() + } + binary.LittleEndian.PutUint64(b.inlineBuffer[b.inlineLength:], v) b.inlineLength += 8 } @@ -17931,18 +18706,76 @@ func (b *keyBuilder) writeNode(node *ast.Node) { } func getTypeListKey(types []*Type) CacheHashKey { + if key, ok := packedTypeListKey(packedInstantiationKeyTag, types, false); ok { + return key + } + return getTypeListKeySlow(types) +} + +func getTypeListKeySlow(types []*Type) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeTypes(types) + return b.releaseHash() + } var b keyBuilder b.writeTypes(types) return b.hash() } func getAliasKey(alias *TypeAlias) CacheHashKey { + if alias == nil { + return CacheHashKey{} + } + return getAliasKeySlow(alias) +} + +func getAliasKeySlow(alias *TypeAlias) CacheHashKey { + if key, ok := packedTypeListKeyWithAlias(0, nil, alias, false); ok { + return key + } + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeAlias(alias) + return b.releaseHash() + } var b keyBuilder b.writeAlias(alias) return b.hash() } func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) CacheHashKey { + if origin == nil { + if key, ok := packedTypeListKeyWithAlias(packedUnionKeyTag, types, alias, false); ok { + return key + } + } + return getUnionKeySlow(types, origin, alias) +} + +func getUnionKeySlow(types []*Type, origin *Type, alias *TypeAlias) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + switch { + case origin == nil: + b.writeTypes(types) + case origin.flags&TypeFlagsUnion != 0: + b.writeByte('|') + b.writeTypes(origin.Types()) + case origin.flags&TypeFlagsIntersection != 0: + b.writeByte('&') + b.writeTypes(origin.Types()) + case origin.flags&TypeFlagsIndex != 0: + b.writeByte('#') + b.writeType(origin) + b.writeByte('|') + b.writeTypes(types) + default: + panic("Unhandled case in getUnionKey") + } + b.writeAlias(alias) + return b.releaseHash() + } var b keyBuilder switch { case origin == nil: @@ -17954,7 +18787,7 @@ func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) CacheHashKey { b.writeByte('&') b.writeTypes(origin.Types()) case origin.flags&TypeFlagsIndex != 0: - // origin type id alone is insufficient, as `keyof x` may resolve to multiple WIP values while `x` is still resolving + // Origin type id alone is insufficient, as `keyof X` may resolve to multiple WIP values while `X` is still resolving b.writeByte('#') b.writeType(origin) b.writeByte('|') @@ -17967,6 +18800,27 @@ func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) CacheHashKey { } func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) CacheHashKey { + var effAlias *TypeAlias + if flags&IntersectionFlagsNoConstraintReduction == 0 { + effAlias = alias + } + if key, ok := packedTypeListKeyWithAlias(packedIntersectionKeyTag, types, effAlias, flags&IntersectionFlagsNoConstraintReduction != 0); ok { + return key + } + return getIntersectionKeySlow(types, flags, alias) +} + +func getIntersectionKeySlow(types []*Type, flags IntersectionFlags, alias *TypeAlias) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeTypes(types) + if flags&IntersectionFlagsNoConstraintReduction == 0 { + b.writeAlias(alias) + } else { + b.writeByte('*') + } + return b.releaseHash() + } var b keyBuilder b.writeTypes(types) if flags&IntersectionFlagsNoConstraintReduction == 0 { @@ -17978,6 +18832,28 @@ func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias } func getTupleKey(elementInfos []TupleElementInfo, readonly bool) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + for _, e := range elementInfos { + switch { + case e.flags&ElementFlagsRequired != 0: + b.writeByte('#') + case e.flags&ElementFlagsOptional != 0: + b.writeByte('?') + case e.flags&ElementFlagsRest != 0: + b.writeByte('.') + default: + b.writeByte('*') + } + if e.labeledDeclaration != nil { + b.writeNode(e.labeledDeclaration) + } + } + if readonly { + b.writeByte('!') + } + return b.releaseHash() + } var b keyBuilder for _, e := range elementInfos { switch { @@ -18005,6 +18881,22 @@ func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) Cache } func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) CacheHashKey { + if key, ok := packedTypeListKeyWithAlias(packedInstantiationKeyTag, typeArguments, alias, singleSignature); ok { + return key + } + return getTypeInstantiationKeySlow(typeArguments, alias, singleSignature) +} + +func getTypeInstantiationKeySlow(typeArguments []*Type, alias *TypeAlias, singleSignature bool) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeTypes(typeArguments) + b.writeAlias(alias) + if singleSignature { + b.writeByte('!') + } + return b.releaseHash() + } var b keyBuilder b.writeTypes(typeArguments) b.writeAlias(alias) @@ -18015,6 +18907,29 @@ func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSign } func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) CacheHashKey { + if alias == nil { + hi := (uint64(packedIndexedAccessKeyTag) << 48) | uint64(accessFlags) + y := ((uint64(objectType.id) | (uint64(indexType.id) << 32)) ^ (hi * 0x9e3779b97f4a7c15)) * 0xbf58476d1ce4e5b9 + return CacheHashKey{Hi: hi, Lo: y ^ (y >> 32)} + } + return getIndexedAccessKeySlow(objectType, indexType, accessFlags, alias) +} + +func getIndexedAccessKeySlow(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) CacheHashKey { + if len(alias.typeArguments) == 0 { + aliasSymId := uint64(ast.GetSymbolId(alias.symbol)) + hi := (uint64(packedIndexedAccessKeyTag) << 48) | (1 << 36) | (uint64(accessFlags) << 32) | aliasSymId + y := ((uint64(objectType.id) | (uint64(indexType.id) << 32)) ^ (hi * 0x9e3779b97f4a7c15)) * 0xbf58476d1ce4e5b9 + return CacheHashKey{Hi: hi, Lo: y ^ (y >> 32)} + } + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeType(objectType) + b.writeType(indexType) + b.writeUint32(uint32(accessFlags)) + b.writeAlias(alias) + return b.releaseHash() + } var b keyBuilder b.writeType(objectType) b.writeType(indexType) @@ -18024,6 +18939,19 @@ func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFl } func getTemplateTypeKey(texts []string, types []*Type) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeTypes(types) + b.writeByte('|') + for _, s := range texts { + b.writeInt(len(s)) + } + b.writeByte('|') + for _, s := range texts { + b.writeString(s) + } + return b.releaseHash() + } var b keyBuilder b.writeTypes(types) b.writeByte('|') @@ -18037,7 +18965,41 @@ func getTemplateTypeKey(texts []string, types []*Type) CacheHashKey { return b.hash() } +func (c *Checker) getTemplateTypeKey(texts []string, types []*Type) CacheHashKey { + b := &c.leafKeyBuilder + b.inlineLength = 0 + if b.overflowBuffer != nil { + b.overflowBuffer = b.overflowBuffer[:0] + } + b.writeTypes(types) + b.writeByte('|') + for _, s := range texts { + b.writeInt(len(s)) + } + b.writeByte('|') + for _, s := range texts { + b.writeString(s) + } + return b.hash() +} + func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) CacheHashKey { + if key, ok := packedTypeListKeyWithAlias(packedConditionalKeyTag, typeArguments, alias, forConstraint); ok { + return key + } + return getConditionalTypeKeySlow(typeArguments, alias, forConstraint) +} + +func getConditionalTypeKeySlow(typeArguments []*Type, alias *TypeAlias, forConstraint bool) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeTypes(typeArguments) + b.writeAlias(alias) + if forConstraint { + b.writeByte('!') + } + return b.releaseHash() + } var b keyBuilder b.writeTypes(typeArguments) b.writeAlias(alias) @@ -18047,22 +19009,38 @@ func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstrain return b.hash() } -func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (CacheHashKey, bool) { +func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (relationKey, bool) { if isIdentity && source.id > target.id { source, target = target, source } - var b keyBuilder - var constrained bool if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) { + return getRelationKeyGeneric(source, target, intersectionState, ignoreConstraints) + } + if uint32(source.id) < 1<<27 && uint32(target.id) < 1<<27 { + return relationKey{value: CacheHashKey{ + Lo: uint64(source.id)<<27 | uint64(target.id), + Hi: uint64(intersectionState), + }}, false + } + return relationKey{value: CacheHashKey{ + Lo: uint64(source.id)<<32 | uint64(target.id), + Hi: uint64(intersectionState), + }, generic: true}, false +} + +func getRelationKeyGeneric(source *Type, target *Type, intersectionState IntersectionState, ignoreConstraints bool) (relationKey, bool) { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() b.writeByte('g') - constrained = b.writeGenericTypeReferences(source, target, ignoreConstraints) - } else { - b.writeByte('s') - b.writeType(source) - b.writeType(target) + constrained := b.writeGenericTypeReferences(source, target, ignoreConstraints) + b.writeUint32(uint32(intersectionState)) + return relationKey{value: b.releaseHash(), generic: true}, constrained } + var b keyBuilder + b.writeByte('g') + constrained := b.writeGenericTypeReferences(source, target, ignoreConstraints) b.writeUint32(uint32(intersectionState)) - return b.hash(), constrained + return relationKey{value: b.hash(), generic: true}, constrained } func getNodeListKey(nodes []*ast.Node) CacheHashKey { @@ -18075,9 +19053,28 @@ func getNodeListKey(nodes []*ast.Node) CacheHashKey { } func isTypeReferenceWithGenericArguments(t *Type) bool { - return isNonDeferredTypeReference(t) && core.Some(t.checker.getTypeArguments(t), func(t *Type) bool { - return t.flags&TypeFlagsTypeParameter != 0 || isTypeReferenceWithGenericArguments(t) - }) + if t.objectFlags&ObjectFlagsReference == 0 || t.objectFlags&(ObjectFlagsHasGenericArgumentsComputed|ObjectFlagsHasGenericArguments) == ObjectFlagsHasGenericArgumentsComputed { + return false + } + return isTypeReferenceWithGenericArgumentsSlow(t) +} + +func isTypeReferenceWithGenericArgumentsSlow(t *Type) bool { + if t.objectFlags&ObjectFlagsHasGenericArguments != 0 { + return true + } + if t.AsTypeReference().node != nil { + return false + } + var result ObjectFlags + for _, arg := range t.checker.getTypeArguments(t) { + if arg.flags&TypeFlagsTypeParameter != 0 || (arg.objectFlags&ObjectFlagsHasGenericArgumentsComputed != 0 && arg.objectFlags&ObjectFlagsHasGenericArguments != 0) || (arg.objectFlags&ObjectFlagsHasGenericArgumentsComputed == 0 && arg.objectFlags&ObjectFlagsReference != 0 && isTypeReferenceWithGenericArgumentsSlow(arg)) { + result = ObjectFlagsHasGenericArguments + break + } + } + t.objectFlags |= ObjectFlagsHasGenericArgumentsComputed | result + return result != 0 } func isNonDeferredTypeReference(t *Type) bool { @@ -18133,8 +19130,8 @@ func (c *Checker) getTypeForBindingElementParent(node *ast.Node, checkMode Check if checkMode == CheckModeNormal { // We can use a cached resolved type if no optionality was included in that type. if symbol := c.getSymbolOfDeclaration(node); symbol != nil { - if resolvedType := c.valueSymbolLinks.Get(symbol).resolvedType; resolvedType != nil && !(c.strictNullChecks && isOptionalDeclaration(node)) { - return resolvedType + if links := c.valueSymbolLinks.TryGet(symbol); links != nil && links.resolvedType != nil && !(c.strictNullChecks && isOptionalDeclaration(node)) { + return links.resolvedType } } } @@ -18263,9 +19260,9 @@ func (c *Checker) getRestType(source *Type, properties []*ast.Node, symbol *ast. } return c.getTypeAliasInstantiation(omitTypeAlias, []*Type{source, omitKeyType}, nil) } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for _, prop := range spreadableProperties { - members[prop.Name()] = c.getSpreadSymbol(prop, false /*readonly*/) + members.Set(prop.Name(), c.getSpreadSymbol(prop, false /*readonly*/)) } result := c.newAnonymousType(symbol, members, nil, nil, c.getIndexInfosOfType(source)) result.objectFlags |= ObjectFlagsObjectRestType @@ -18356,7 +19353,7 @@ func (c *Checker) getTypeFromBindingPattern(pattern *ast.Node, includePatternInT // Return the type implied by an object binding pattern func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type { - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() var stringIndexInfo *IndexInfo objectFlags := ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral for _, e := range pattern.Elements() { @@ -18375,7 +19372,7 @@ func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatt flags := ast.SymbolFlagsProperty | core.IfElse(e.Initializer() != nil, ast.SymbolFlagsOptional, 0) symbol := c.newSymbol(flags, text) c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, includePatternInType, reportErrors) - members[symbol.Name()] = symbol + members.Set(symbol.Name(), symbol) } var indexInfos []*IndexInfo if stringIndexInfo != nil { @@ -18844,14 +19841,14 @@ func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContex return cached } } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for _, prop := range c.getPropertiesOfObjectType(t) { - members[prop.Name()] = c.getWidenedProperty(prop, context) + members.Set(prop.Name(), c.getWidenedProperty(prop, context)) } if context != nil { for _, prop := range c.getPropertiesOfContext(context) { - if _, ok := members[prop.Name()]; !ok { - members[prop.Name()] = c.getUndefinedProperty(prop) + if _, ok := members.Lookup(prop.Name()); !ok { + members.Set(prop.Name(), c.getUndefinedProperty(prop)) } } } @@ -19043,7 +20040,7 @@ func (c *Checker) getTypeOfAlias(symbol *ast.Symbol) *Type { return c.errorType } targetSymbol := c.resolveAlias(symbol) - exportSymbol := c.getTargetOfAliasDeclaration(c.getDeclarationOfAliasSymbol(symbol)) + exportSymbol := c.getTargetOfAliasDeclaration(c.getDeclarationOfAliasSymbol(symbol), false /*dontRecursivelyResolve*/) // It only makes sense to get the type of a value symbol. If the result of resolving // the alias is not a value, then it has no type. To get the type associated with a // type symbol, call getDeclaredTypeOfSymbol. @@ -19200,12 +20197,13 @@ func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeS resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName) if resolutionCycleStartIndex >= 0 { // A cycle was found + c.resolutionCycleCount++ for i := resolutionCycleStartIndex; i < len(c.typeResolutions); i++ { c.typeResolutions[i].result = false } return false } - c.typeResolutions = append(c.typeResolutions, TypeResolution{target: target, propertyName: propertyName, result: true}) + c.typeResolutions = append(c.typeResolutions, TypeResolution{target: target, propertyName: propertyName, result: true, serial: c.flowMemo.sourceNow()}) return true } @@ -19228,6 +20226,7 @@ func (c *Checker) findResolutionCycleStartIndex(target TypeSystemEntity, propert return -1 } if resolution.target == target && resolution.propertyName == propertyName { + c.flowMemo.taint(resolution.serial) return i } } @@ -19294,47 +20293,22 @@ func (c *Checker) getPropertiesOfType(t *Type) []*ast.Symbol { func (c *Checker) getPropertiesOfObjectType(t *Type) []*ast.Symbol { if t.flags&TypeFlagsObject != 0 { - return c.resolveStructuredTypeMembers(t).getProperties() - } - return nil -} - -// For classes and interfaces, the layout properties of the type are uninstantiated symbols from -// the shared member layout. When operations only process the declaration, flags, and/or name -// of members (and not their type), using the layout properties helps defer instantiation of -// the members. For example, `keyof T` can be computed entirely from layout properties. -func (c *Checker) getLayoutPropertiesOfType(t *Type) []*ast.Symbol { - t = c.getReducedApparentType(t) - lazy := c.getLazyStateOfType(t) - if lazy == nil { - return c.getPropertiesOfType(t) - } - layout := lazy.layout - if layout.layoutProperties == nil { - layout.layoutProperties = make([]*ast.Symbol, len(layout.memberInfos)) - for i, memberInfo := range layout.memberInfos { - layout.layoutProperties[i] = memberInfo.symbol - } - } - return layout.layoutProperties -} - -func (c *Checker) getLazyStateOfType(t *Type) *LazyState { - if t.flags&TypeFlagsObject != 0 { - return c.resolveStructuredTypeMembers(t).getLazyState() + return c.resolveStructuredTypeMembers(t).getPropertiesSlice() } return nil } func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*ast.Symbol { d := t.AsUnionOrIntersectionType() - if d.resolvedProperties == nil { - var checked collections.Set[string] + if d.getPropertiesSlice() == nil { + checked := c.getTemporaryNameSet() + defer c.releaseTemporaryNameSet(checked) props := []*ast.Symbol{} for _, current := range d.types { - for _, prop := range c.getPropertiesOfType(current) { - if !checked.Has(prop.Name()) { - checked.Add(prop.Name()) + currentProps, _ := c.getPropertiesOfTypeLazily(current) + for _, prop := range currentProps { + if _, ok := checked[prop.Name()]; !ok { + checked[prop.Name()] = struct{}{} combinedProp := c.getPropertyOfUnionOrIntersectionType(t, prop.Name(), t.flags&TypeFlagsIntersection != 0 /*skipObjectFunctionPropertyAugment*/) if combinedProp != nil { props = append(props, combinedProp) @@ -19347,13 +20321,91 @@ func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*ast.Symbol break } } - d.resolvedProperties = props + d.ensureExtra().properties = props } - return d.resolvedProperties + return d.getPropertiesSlice() } func (c *Checker) getPropertyOfType(t *Type, name string) *ast.Symbol { - return c.getPropertyOfTypeEx(t, name, false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) + return c.getPropertyOfTypeWorker(t, name, false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/, true /*instantiate*/) +} + +func (c *Checker) hasPropertyOfType(t *Type, name string) bool { + return c.getPropertyOfTypeWorker(t, name, false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/, false /*instantiate*/) != nil +} + +func (c *Checker) isResolvedPlainObjectType(t *Type) bool { + return t.flags == TypeFlagsObject && t.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) == ObjectFlagsMembersResolved && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) +} + +func (c *Checker) getResolvedPropertyOfPlainObjectType(t *Type, name string) *ast.Symbol { + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + if symbol := t.AsStructuredType().getMember(name); symbol != nil { + if symbol.Flags()&ast.SymbolFlagsValue != 0 { + return symbol + } + } else if !c.mayBeGlobalObjectMember(name) { + return nil + } + } + return c.getPropertyOfType(t, name) +} + +func nameFilterBit(name string) (uint8, uint64, uint8, uint64) { + if len(name) == 0 { + return 0, 1, 0, 1 + } + h := uint32(len(name))*131 + uint32(name[0])*31 + uint32(name[len(name)-1]) + if len(name) > 2 { + h = h*31 + uint32(name[1]) + } + h *= 0x9e3779b9 + return uint8((h >> 6) & 3), uint64(1) << (h & 63), uint8((h >> 22) & 3), uint64(1) << ((h >> 16) & 63) +} + +func addTypeToGlobalFilter(t *Type, filter *[4]uint64) bool { + if t == nil || t.flags&TypeFlagsObject == 0 || t.objectFlags&ObjectFlagsMembersResolved == 0 { + return false + } + st := t.AsStructuredType() + if lazy := st.getLazyState(); lazy != nil { + for _, info := range lazy.layout.memberInfos { + w1, b1, w2, b2 := nameFilterBit(info.symbol.Name()) + filter[w1] |= b1 + filter[w2] |= b2 + } + return true + } + if mIdx := st.getMembersIndex(); mIdx != nil && mIdx.fallback != nil { + for k := range mIdx.fallback.All() { + w1, b1, w2, b2 := nameFilterBit(k) + filter[w1] |= b1 + filter[w2] |= b2 + } + } else { + for _, p := range st.getPropertiesSlice() { + w1, b1, w2, b2 := nameFilterBit(p.Name()) + filter[w1] |= b1 + filter[w2] |= b2 + } + } + return true +} + +func (c *Checker) mayBeGlobalObjectMember(name string) bool { + if !c.globalObjectFilterReady { + var filter [4]uint64 + if !addTypeToGlobalFilter(c.globalObjectType, &filter) || + !addTypeToGlobalFilter(c.globalFunctionType, &filter) || + !addTypeToGlobalFilter(c.globalCallableFunctionType, &filter) || + !addTypeToGlobalFilter(c.globalNewableFunctionType, &filter) { + return true + } + c.globalObjectAugmentFilter = filter + c.globalObjectFilterReady = true + } + w1, b1, w2, b2 := nameFilterBit(name) + return c.globalObjectAugmentFilter[w1]&b1 != 0 && c.globalObjectAugmentFilter[w2]&b2 != 0 } /** @@ -19365,11 +20417,14 @@ func (c *Checker) getPropertyOfType(t *Type, name string) *ast.Symbol { * @param name a name of property to look up in a given type */ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool) *ast.Symbol { + return c.getPropertyOfTypeWorker(t, name, skipObjectFunctionPropertyAugment, includeTypeOnlyMembers, true /*instantiate*/) +} + +func (c *Checker) getPropertyOfTypeWorker(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool, instantiate bool) *ast.Symbol { t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: - resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.getMember(name) + symbol := c.getMemberOfStructuredTypeEx(t, name, instantiate) if symbol != nil { if !includeTypeOnlyMembers && t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.Get(t.symbol).typeOnlyExportStarMap[name] != nil { // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over @@ -19381,16 +20436,16 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr return symbol } } - if skipObjectFunctionPropertyAugment { + if skipObjectFunctionPropertyAugment || !c.mayBeGlobalObjectMember(name) { return nil } var functionType *Type switch { case t == c.anyFunctionType: functionType = c.globalFunctionType - case len(resolved.CallSignatures()) != 0: + case len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) != 0: functionType = c.globalCallableFunctionType - case len(resolved.ConstructSignatures()) != 0: + case len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) != 0: functionType = c.globalNewableFunctionType } if functionType != nil { @@ -19432,7 +20487,15 @@ func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []* if t.flags&TypeFlagsStructuredType == 0 || t.objectFlags&(ObjectFlagsMapped|ObjectFlagsReverseMapped) != 0 { return nil } - resolved := c.resolveStructuredTypeMembers(t) + if t.objectFlags&ObjectFlagsMembersResolved == 0 { + if t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsAnonymous != 0 && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + if d := t.AsObjectType(); d.target != nil && len(c.getSignaturesOfStructuredType(d.target, kind)) == 0 { + return nil + } + } + c.resolveStructuredTypeMembers(t) + } + resolved := t.AsStructuredType() if resolved.extra == nil { return nil } @@ -19448,7 +20511,18 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { - return c.resolveStructuredTypeMembers(t).getIndexInfos() + if t.objectFlags&ObjectFlagsMembersResolved == 0 { + if lazy := c.getLazyMappedTable(t); lazy != nil { + return c.getLazyMappedTypeIndexInfos(t, lazy) + } + if t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsAnonymous != 0 && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + if d := t.AsObjectType(); d.target != nil && len(c.getIndexInfosOfStructuredType(d.target)) == 0 { + return nil + } + } + return c.resolveStructuredTypeMembers(t).getIndexInfos() + } + return t.AsStructuredType().getIndexInfos() } return nil } @@ -19481,45 +20555,65 @@ func (c *Checker) getApplicableIndexInfo(t *Type, keyType *Type) *IndexInfo { } func (c *Checker) getApplicableIndexInfoForName(t *Type, name string) *IndexInfo { + indexInfos := c.getIndexInfosOfType(t) + if len(indexInfos) == 0 { + return nil + } if isLateBoundName(name) { - return c.getApplicableIndexInfo(t, c.esSymbolType) + return c.findApplicableIndexInfo(indexInfos, c.esSymbolType) } - return c.getApplicableIndexInfo(t, c.getStringLiteralType(name)) + return c.findApplicableIndexInfo(indexInfos, c.getStringLiteralType(name)) } func (c *Checker) findApplicableIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { + if len(indexInfos) == 0 { + return nil + } // Index signatures for type 'string' are considered only when no other index signatures apply. var stringIndexInfo *IndexInfo - applicableInfos := make([]*IndexInfo, 0, 8) + var firstApplicable *IndexInfo + var applicableInfosBuf [4]*IndexInfo + var applicableInfos []*IndexInfo for _, info := range indexInfos { if info.keyType == c.stringType { stringIndexInfo = info } else if c.isApplicableIndexType(keyType, info.keyType) { - applicableInfos = append(applicableInfos, info) + if firstApplicable == nil { + firstApplicable = info + } else { + if applicableInfos == nil { + applicableInfos = append(applicableInfosBuf[:0], firstApplicable, info) + } else { + applicableInfos = append(applicableInfos, info) + } + } } } - // When more than one index signature is applicable we create a synthetic IndexInfo. Instead of computing - // the intersected key type, we just use unknownType for the key type as nothing actually depends on the - // keyType property of the returned IndexInfo. - switch len(applicableInfos) { - case 0: + // When more than one index signature is applicable we create a synthetic IndexInfo with an intersection of the + // value types and a readonly flag that is true if all applicable index signatures are readonly. Note that for + // the intersected key type, we just use unknownType for the key type as nothing actually depends on it. + if applicableInfos == nil { + if firstApplicable != nil { + return firstApplicable + } if stringIndexInfo != nil && c.isApplicableIndexType(keyType, c.stringType) { return stringIndexInfo } return nil - case 1: - return applicableInfos[0] - default: - isReadonly := true - types := make([]*Type, len(applicableInfos)) - for i, info := range applicableInfos { - types[i] = info.valueType - if !info.isReadonly { - isReadonly = false - } + } + isReadonly := true + var typesBuf [4]*Type + types := typesBuf[:0] + if len(applicableInfos) > len(typesBuf) { + types = make([]*Type, 0, len(applicableInfos)) + } + for _, info := range applicableInfos { + types = append(types, info.valueType) + if !info.isReadonly { + isReadonly = false } - return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil) } + return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil) } func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { @@ -19532,31 +20626,207 @@ func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { if t.objectFlags&ObjectFlagsMembersResolved == 0 { + return c.resolveStructuredTypeMembersSlow(t) + } + return t.AsStructuredType() +} + +func (c *Checker) resolveStructuredTypeMembersSlow(t *Type) *StructuredType { + switch { + case t.flags&TypeFlagsObject != 0: switch { - case t.flags&TypeFlagsObject != 0: - switch { - case t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0: - c.resolveObjectTypeMembers(t) - case t.objectFlags&ObjectFlagsReverseMapped != 0: - c.resolveReverseMappedTypeMembers(t) - case t.objectFlags&ObjectFlagsAnonymous != 0: - c.resolveAnonymousTypeMembers(t) - case t.objectFlags&ObjectFlagsMapped != 0: - c.resolveMappedTypeMembers(t) - default: - panic("Unhandled case in resolveStructuredTypeMembers") - } - case t.flags&TypeFlagsUnion != 0: - c.resolveUnionTypeMembers(t) - case t.flags&TypeFlagsIntersection != 0: - c.resolveIntersectionTypeMembers(t) + case t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0: + c.resolveObjectTypeMembers(t) + case t.objectFlags&ObjectFlagsReverseMapped != 0: + c.resolveReverseMappedTypeMembers(t) + case t.objectFlags&ObjectFlagsAnonymous != 0: + c.resolveAnonymousTypeMembers(t) + case t.objectFlags&ObjectFlagsMapped != 0: + c.resolveMappedTypeMembers(t) default: panic("Unhandled case in resolveStructuredTypeMembers") } + case t.flags&TypeFlagsUnion != 0: + c.resolveUnionTypeMembers(t) + case t.flags&TypeFlagsIntersection != 0: + c.resolveIntersectionTypeMembers(t) + default: + panic("Unhandled case in resolveStructuredTypeMembers") } return t.AsStructuredType() } +func (c *Checker) getLazyStateOfType(t *Type) *LazyState { + if t.flags&TypeFlagsObject != 0 { + return c.resolveStructuredTypeMembers(t).getLazyState() + } + return nil +} + +func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { + return c.getMemberOfStructuredTypeEx(t, name, true /*instantiate*/) +} + +func (c *Checker) isBinaryKeyedTypeLiteral(t *Type) bool { + return t != nil && t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.IsBinaryKeyedTypeLiteral() +} + +func (c *Checker) getMemberOfStructuredTypeEx(t *Type, name string, instantiate bool) *ast.Symbol { + if t.objectFlags&ObjectFlagsMembersResolved == 0 && c.isBinaryKeyedTypeLiteral(t) { + return t.symbol.LookupLazyMember(name) + } + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + if !instantiate { + if lazy := t.AsStructuredType().getLazyState(); lazy != nil { + slotIndex := lazy.layout.lookupMemberIndex(name) + if slotIndex < 0 { + return nil + } + if t.AsStructuredType().extra != nil && slotIndex < len(t.AsStructuredType().extra.properties) { + if prop := t.AsStructuredType().extra.properties[slotIndex]; prop != nil { + return prop + } + } + return lazy.layout.memberInfos[slotIndex].symbol + } + } + return t.AsStructuredType().getMember(name) + } + if t.objectFlags&ObjectFlagsMapped != 0 && !isReservedMemberName(name) { + if lazy := c.getLazyMappedTable(t); lazy != nil { + if !instantiate && !lazy.resolving && !isLateBoundName(name) && !strings.HasPrefix(name, "\xFE#") { + if existing, ok := lazy.members[name]; ok { + return existing + } + lazy.resolving = true + modifiersProp := c.getMemberOfStructuredTypeEx(lazy.modifiersType, name, false /*instantiate*/) + lazy.resolving = false + if modifiersProp == nil { + return nil + } + if c.isNamedMember(modifiersProp, name) && getDeclarationModifierFlagsFromSymbol(modifiersProp)&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { + return modifiersProp + } + } + if member, ok := c.getLazyMappedTypeMember(t, lazy, name); ok { + return member + } + } + } + if !isReservedMemberName(name) && t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsAnonymous != 0 && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + if d := t.AsObjectType(); d.target != nil { + targetResolved := c.resolveStructuredTypeMembers(d.target) + if targetMembers := targetResolved.getMembersIndex(); targetMembers == nil || targetMembers.fallback == nil { + targetProp := targetResolved.getMember(name) + if targetProp == nil || !instantiate { + return targetProp + } + } + } + } + resolved := c.resolveStructuredTypeMembers(t) + if !instantiate { + if lazy := resolved.getLazyState(); lazy != nil { + slotIndex := lazy.layout.lookupMemberIndex(name) + if slotIndex < 0 { + return nil + } + if resolved.extra != nil && slotIndex < len(resolved.extra.properties) { + if prop := resolved.extra.properties[slotIndex]; prop != nil { + return prop + } + } + return lazy.layout.memberInfos[slotIndex].symbol + } + } + return resolved.getMember(name) +} + +func (c *Checker) everyPropertyOfStructuredType(t *Type, f func(*ast.Symbol) bool) bool { + props, _ := c.getPropertiesOfTypeLazily(t) + for _, p := range props { + if !f(p) { + return false + } + } + return true +} + +func (c *Checker) hasPropertiesOfStructuredType(t *Type) bool { + if c.isBinaryKeyedTypeLiteral(t) { + return true + } + if t.flags&TypeFlagsObject != 0 && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + return c.resolveStructuredTypeMembers(t).hasProperties() + } + return !c.everyPropertyOfStructuredType(t, func(*ast.Symbol) bool { return false }) +} + +// For classes and interfaces, the layout properties of the type are uninstantiated symbols from +// the shared member layout. When operations only process the declaration, flags, and/or name +// of members (and not their type), using the layout properties helps defer instantiation of +// the members. For example, 'keyof T' can be computed entirely from layout properties. +func (c *Checker) getLayoutPropertiesOfType(t *Type) []*ast.Symbol { + props, _ := c.getPropertiesOfTypeLazily(t) + return props +} + +func (c *Checker) getPropertiesOfTypeLazily(t *Type) (properties []*ast.Symbol, lazyType *Type) { + t = c.getReducedApparentType(t) + if t.flags&TypeFlagsUnionOrIntersection != 0 { + return c.getPropertiesOfUnionOrIntersectionType(t), nil + } + if t.flags&TypeFlagsObject != 0 { + if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + resolved := c.resolveStructuredTypeMembers(t) + if lazy := resolved.getLazyState(); lazy != nil { + if lazy.propertiesResolved { + return resolved.getProperties(), nil + } + layout := lazy.layout + if layout.layoutProperties == nil { + if len(layout.memberInfos) == 0 { + return nil, nil + } + layout.layoutProperties = c.symbolListArena.NewSlice(len(layout.memberInfos)) + for i, memberInfo := range layout.memberInfos { + layout.layoutProperties[i] = memberInfo.symbol + } + } + return layout.layoutProperties, t + } + return resolved.getProperties(), nil + } + if t.objectFlags&ObjectFlagsMembersResolved == 0 { + if t.objectFlags&ObjectFlagsAnonymous != 0 && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + if d := t.AsObjectType(); d.target != nil { + targetResolved := c.resolveStructuredTypeMembers(d.target) + if targetMembers := targetResolved.getMembersIndex(); targetMembers == nil || targetMembers.fallback == nil { + return targetResolved.getProperties(), t + } + } + } + if lazy := c.getLazyMappedTable(t); lazy != nil && !lazy.resolving && lazy.templateModifiers&(MappedTypeModifiersIncludeOptional|MappedTypeModifiersExcludeOptional) == 0 && len(lazy.members) == 0 { + lazy.resolving = true + modProps, _ := c.getPropertiesOfTypeLazily(lazy.modifiersType) + lazy.resolving = false + canUseModProps := true + for _, p := range modProps { + if isLateBoundName(p.Name()) || strings.HasPrefix(p.Name(), "\xFE#") || p.Flags()&ast.SymbolFlagsPrototype != 0 || getDeclarationModifierFlagsFromSymbol(p)&ast.ModifierFlagsNonPublicAccessibilityModifier != 0 { + canUseModProps = false + break + } + } + if canUseModProps && t.objectFlags&ObjectFlagsMembersResolved == 0 { + return modProps, t + } + } + } + return c.resolveStructuredTypeMembers(t).getProperties(), nil + } + return nil, nil +} + func (c *Checker) resolveObjectTypeMembers(t *Type) { var mapper *TypeMapper var typeParameters []*Type @@ -19565,12 +20835,15 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { var callSignatures []*Signature var constructSignatures []*Signature var indexInfos []*IndexInfo - source := c.getTargetType(t) + source := getTargetType(t) if t.objectFlags&ObjectFlagsReference != 0 { typeParameters = source.AsInterfaceType().allTypeParameters typeArguments = c.getTypeArguments(t) if len(typeArguments) == len(typeParameters)-1 { - typeArguments = core.Concatenate(typeArguments, []*Type{t}) + padded := c.typeListArena.NewSlice(len(typeArguments) + 1) + copy(padded, typeArguments) + padded[len(typeArguments)] = t + typeArguments = padded } } resolved := c.resolveDeclaredMembers(source) @@ -19588,7 +20861,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { instantiatedBaseTypes := baseTypes thisArgument := core.LastOrNil(typeArguments) if len(baseTypes) != 0 && thisArgument != nil { - instantiatedBaseTypes = make([]*Type, len(baseTypes)) + instantiatedBaseTypes = c.typeListArena.NewSlice(len(baseTypes)) for i, baseType := range baseTypes { instantiatedBaseTypes[i] = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) } @@ -19620,7 +20893,18 @@ func (c *Checker) getMemberLayout(t *Type, baseTypes []*Type) *MemberLayout { if t.objectFlags&ObjectFlagsReference == 0 { return c.createMemberLayout(t, baseTypes) } - source := c.getTargetType(t) + source := getTargetType(t) + if len(baseTypes) == 0 { + d := source.AsInterfaceType() + count := int32(d.declaredMembers.Len()) + if d.declaredMemberLayout != nil && d.declaredMemberLayoutCount == count { + return d.declaredMemberLayout + } + layout := c.createMemberLayout(source, nil) + d.declaredMemberLayout = layout + d.declaredMemberLayoutCount = count + return layout + } key := c.getMemberLayoutKey(source, baseTypes) layout := c.memberLayoutCache[key] if layout == nil { @@ -19634,11 +20918,26 @@ func (c *Checker) getMemberLayout(t *Type, baseTypes []*Type) *MemberLayout { } func (c *Checker) getMemberLayoutKey(t *Type, baseTypes []*Type) CacheHashKey { + if len(baseTypes) == 1 { + baseType := baseTypes[0] + declCount := uint64(uint32(t.AsInterfaceType().declaredMembers.Len())) + if baseType.objectFlags&ObjectFlagsReference != 0 { + c.resolveStructuredTypeMembers(baseType) + return CacheHashKey{ + Hi: (uint64(0x91a1) << 48) | (1 << 32) | uint64(uint32(t.id)), + Lo: (declCount << 32) | uint64(baseType.AsTypeReference().lazy.layout.id), + } + } + return CacheHashKey{ + Hi: (uint64(0x91a1) << 48) | uint64(uint32(t.id)), + Lo: (declCount << 32) | uint64(uint32(baseType.id)), + } + } var b keyBuilder b.reserve(20 + 5*len(baseTypes)) b.writeType(t) // Late binding may add new declared members, so include the declared member count in the key - b.writeInt(len(t.AsInterfaceType().declaredMembers)) + b.writeInt(t.AsInterfaceType().declaredMembers.Len()) b.writeInt(len(baseTypes)) for _, baseType := range baseTypes { if baseType.objectFlags&ObjectFlagsReference != 0 { @@ -19654,60 +20953,96 @@ func (c *Checker) getMemberLayoutKey(t *Type, baseTypes []*Type) CacheHashKey { } func (c *Checker) createMemberLayout(t *Type, baseTypes []*Type) *MemberLayout { - // Reuse c.layoutMemberInfos to avoid repeated allocations - layoutMemberInfos := c.layoutMemberInfos - c.layoutMemberInfos = nil - addMember := func(symbol *ast.Symbol, baseIndex int) { - if _, ok := layoutMemberInfos[symbol.Name()]; !ok { - if layoutMemberInfos == nil { - layoutMemberInfos = make(map[string]MemberInfo) + declaredMembers := t.AsInterfaceType().declaredMembers + var memberInfos []MemberInfo + var memberInfoMap map[string]int32 + if len(baseTypes) == 0 && symbolTableKeysMatchNames(declaredMembers) { + n := 0 + for id, symbol := range declaredMembers.All() { + if c.isNamedMember(symbol, id) { + n++ + } + } + if n != 0 { + memberInfos = c.memberInfoListArena.NewSlice(n) + i := 0 + for id, symbol := range declaredMembers.All() { + if c.isNamedMember(symbol, id) { + memberInfos[i] = MemberInfo{symbol: symbol, baseIndex: -1} + i++ + } + } + slices.SortFunc(memberInfos, func(a, b MemberInfo) int { + return c.compareSymbols(a.symbol, b.symbol) + }) + if n > 3 { + memberInfoMap = make(map[string]int32, n) + for idx := range memberInfos { + memberInfoMap[memberInfos[idx].symbol.Name()] = int32(idx + 1) + } } - layoutMemberInfos[symbol.Name()] = MemberInfo{symbol: symbol, baseIndex: int32(baseIndex)} - } - } - // Add declared members - for id, symbol := range t.AsInterfaceType().declaredMembers { - if c.isNamedMember(symbol, id) { - addMember(symbol, -1) } - } - // Add inherited members - for baseIndex, baseType := range baseTypes { - if baseType.objectFlags&(ObjectFlagsReference|ObjectFlagsClassOrInterface) != 0 { - c.resolveStructuredTypeMembers(baseType) - for _, info := range baseType.AsTypeReference().lazy.layout.memberInfos { - if !isStaticPrivateIdentifierProperty(info.symbol) { - addMember(info.symbol, baseIndex) + } else { + // Reuse c.layoutMemberInfos to avoid repeated allocations + layoutMemberInfos := c.layoutMemberInfos + c.layoutMemberInfos = nil + addMember := func(symbol *ast.Symbol, baseIndex int) { + if _, ok := layoutMemberInfos[symbol.Name()]; !ok { + if layoutMemberInfos == nil { + layoutMemberInfos = make(map[string]MemberInfo) + } + layoutMemberInfos[symbol.Name()] = MemberInfo{symbol: symbol, baseIndex: int32(baseIndex)} + } + } + // Add declared members + for id, symbol := range declaredMembers.All() { + if c.isNamedMember(symbol, id) { + addMember(symbol, -1) + } + } + // Add inherited members + for baseIndex, baseType := range baseTypes { + if baseType.objectFlags&(ObjectFlagsReference|ObjectFlagsClassOrInterface) != 0 { + c.resolveStructuredTypeMembers(baseType) + for _, info := range baseType.AsTypeReference().lazy.layout.memberInfos { + if !isStaticPrivateIdentifierProperty(info.symbol) { + addMember(info.symbol, baseIndex) + } } - } - } else { - for _, symbol := range c.getPropertiesOfType(baseType) { - if !isStaticPrivateIdentifierProperty(symbol) { - addMember(symbol, baseIndex) + } else { + for _, symbol := range c.getPropertiesOfType(baseType) { + if !isStaticPrivateIdentifierProperty(symbol) { + addMember(symbol, baseIndex) + } } } } - } - memberInfos := slices.AppendSeq(make([]MemberInfo, 0, len(layoutMemberInfos)), maps.Values(layoutMemberInfos)) - clear(layoutMemberInfos) - c.layoutMemberInfos = layoutMemberInfos - // Sort by symbols, but put declared members (baseIndex < 0) first - slices.SortFunc(memberInfos, func(a, b MemberInfo) int { - if a.baseIndex < 0 && b.baseIndex >= 0 { - return -1 - } - if a.baseIndex >= 0 && b.baseIndex < 0 { - return 1 + if n := len(layoutMemberInfos); n != 0 { + memberInfos = c.memberInfoListArena.NewSlice(n) + i := 0 + for _, info := range layoutMemberInfos { + memberInfos[i] = info + i++ + } + clear(layoutMemberInfos) + // Sort by symbols, but put declared members (baseIndex < 0) first + slices.SortFunc(memberInfos, func(a, b MemberInfo) int { + if a.baseIndex < 0 && b.baseIndex >= 0 { + return -1 + } + if a.baseIndex >= 0 && b.baseIndex < 0 { + return 1 + } + return c.compareSymbols(a.symbol, b.symbol) + }) + if n > 3 { + memberInfoMap = make(map[string]int32, n) + for idx := range memberInfos { + memberInfoMap[memberInfos[idx].symbol.Name()] = int32(idx + 1) + } + } } - return c.CompareSymbols(a.symbol, b.symbol) - }) - // Store slot indices in the sorted memberInfos and build the memberInfoMap. Slot indices are used to - // identify slots in the instantiated type's properties list. - memberInfoMap := make(map[string]*MemberInfo, len(memberInfos)) - for i := range memberInfos { - memberInfo := &memberInfos[i] - memberInfo.slotIndex = int32(i) - memberInfoMap[memberInfo.symbol.Name()] = memberInfo + c.layoutMemberInfos = layoutMemberInfos } layout := c.memberLayoutArena.New() layout.id = c.memberLayoutCount @@ -19760,6 +21095,25 @@ func (c *Checker) getBaseTypes(t *Type) []*Type { // members partially resolved. Here we ensure any such partial resolution is reset. // See https://github.com/microsoft/TypeScript/issues/16861 for an example. t.objectFlags &^= ObjectFlagsMembersResolved + t.cacheFlags &^= typeCacheIsWeakComputed | typeCacheIsWeak + data.lazy = nil + if data.instantiations != nil { + if inst := data.instantiations.first.val; inst != nil && inst != t && inst.AsTypeReference().lazy != nil { + inst.objectFlags &^= ObjectFlagsMembersResolved + inst.cacheFlags &^= typeCacheIsWeakComputed | typeCacheIsWeak + inst.AsTypeReference().lazy = nil + } + for i := range data.instantiations.slots { + if inst := data.instantiations.slots[i].val; inst != nil && inst != t && inst.AsTypeReference().lazy != nil { + inst.objectFlags &^= ObjectFlagsMembersResolved + inst.cacheFlags &^= typeCacheIsWeakComputed | typeCacheIsWeak + inst.AsTypeReference().lazy = nil + } + } + } + if t == c.globalObjectType { + c.globalObjectFilterReady = false + } data.baseTypesResolved = true } return data.resolvedBaseTypes @@ -19861,6 +21215,7 @@ func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Typ newReturnSignature.setTypeParameters(c, inferredTypeParameters) newReturnType := c.getOrCreateTypeFromSignature(newReturnSignature) newReturnType.AsObjectType().mapper = instantiatedSignature.mapper + markMapperEscaped(instantiatedSignature.mapper) newInstantiatedSignature := c.cloneSignature(instantiatedSignature) newInstantiatedSignature.resolvedReturnType = newReturnType return newInstantiatedSignature @@ -19870,9 +21225,10 @@ func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Typ } func (c *Checker) cloneSignature(sig *Signature) *Signature { - result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, sig.TypeParameters(), sig.ThisParameter(), sig.parameters, nil, nil, int(sig.minArgumentCount)) + result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, sig.TypeParameters(), c.getSignatureThisParameter(sig), c.getSignatureParameters(sig), nil, nil, int(sig.minArgumentCount)) result.target = sig.target result.mapper = sig.mapper + markMapperEscaped(sig.mapper) result.setComposite(c, sig.getComposite()) return result } @@ -19914,13 +21270,14 @@ func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - if allowMembers || !resolved.hasProperties() && len(resolved.getIndexInfos()) == 0 { - if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 { - return resolved.CallSignatures()[0] + if allowMembers || !c.hasPropertiesOfStructuredType(t) && len(c.getIndexInfosOfStructuredType(t)) == 0 { + callSignatures := c.getSignaturesOfStructuredType(t, SignatureKindCall) + constructSignatures := c.getSignaturesOfStructuredType(t, SignatureKindConstruct) + if kind == SignatureKindCall && len(callSignatures) == 1 && len(constructSignatures) == 0 { + return callSignatures[0] } - if kind == SignatureKindConstruct && len(resolved.ConstructSignatures()) == 1 && len(resolved.CallSignatures()) == 0 { - return resolved.ConstructSignatures()[0] + if kind == SignatureKindConstruct && len(constructSignatures) == 1 && len(callSignatures) == 0 { + return constructSignatures[0] } } } @@ -19956,13 +21313,20 @@ func (c *Checker) getOrCreateTypeFromSignature(sig *Signature) *Type { } func (c *Checker) getErasedSignature(signature *Signature) *Signature { - if len(signature.TypeParameters()) == 0 { + typeParameters := signature.TypeParameters() + if len(typeParameters) == 0 { return signature } key := CachedSignatureKey{sig: signature, key: SignatureKeyErased} erased := c.cachedSignatures[key] if erased == nil { - erased = c.instantiateSignatureEx(signature, newArrayToSingleTypeMapper(signature.TypeParameters(), c.anyType), true /*eraseTypeParameters*/) + var mapper *TypeMapper + if len(typeParameters) == 1 { + mapper = newSimpleTypeMapper(typeParameters[0], c.anyType) + } else { + mapper = newArrayToSingleTypeMapper(typeParameters, c.anyType) + } + erased = c.instantiateSignatureEx(signature, mapper, true /*eraseTypeParameters*/) c.cachedSignatures[key] = erased } return erased @@ -20054,7 +21418,11 @@ func (c *Checker) instantiateSignatureInContextOf(signature *Signature, contextu c.inferTypes(context.inferences, source, target, InferencePriorityReturnType, false) }) } - return c.getSignatureInstantiation(signature, c.getInferredTypes(context), ast.IsInJSFile(contextualSignature.declaration), nil /*inferredTypeParameters*/) + inferredTypes := c.getInferredTypes(context) + if !context.escaped { + c.releaseConditionalInferenceContext(context, nil, nil) + } + return c.getSignatureInstantiation(signature, inferredTypes, ast.IsInJSFile(contextualSignature.declaration), nil /*inferredTypeParameters*/) } func (c *Checker) resolveBaseTypesOfInterface(t *Type) { @@ -20160,11 +21528,11 @@ func (c *Checker) getTypeWithThisArgument(t *Type, thisArgument *Type, needAppar func (c *Checker) addInheritedMembers(symbols ast.SymbolTable, baseSymbols []*ast.Symbol) ast.SymbolTable { for _, base := range baseSymbols { if !isStaticPrivateIdentifierProperty(base) { - if s, ok := symbols[base.Name()]; !ok || s.Flags()&ast.SymbolFlagsValue == 0 { + if s, ok := symbols.Lookup(base.Name()); !ok || s.Flags()&ast.SymbolFlagsValue == 0 { if symbols == nil { - symbols = make(ast.SymbolTable) + symbols = c.acquireSymbolTable() } - symbols[base.Name()] = base + symbols.Set(base.Name(), base) } } } @@ -20177,8 +21545,8 @@ func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceType { members := c.getMembersOfSymbol(t.symbol) d.declaredMembersResolved = true d.declaredMembers = members - d.declaredCallSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameCall]) - d.declaredConstructSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameNew]) + d.declaredCallSignatures = c.getSignaturesOfSymbol(d.declaredMembers.Get(ast.InternalSymbolNameCall)) + d.declaredConstructSignatures = c.getSignaturesOfSymbol(d.declaredMembers.Get(ast.InternalSymbolNameNew)) d.declaredIndexInfos = c.getIndexInfosOfSymbol(t.symbol) } return d @@ -20187,7 +21555,7 @@ func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceType { func (c *Checker) getIndexInfosOfSymbol(symbol *ast.Symbol) []*IndexInfo { indexSymbol := c.getIndexSymbol(symbol) if indexSymbol != nil { - return c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(c.getMembersOfSymbol(symbol)))) + return c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(c.getMembersOfSymbol(symbol).Values())) } return nil } @@ -20362,7 +21730,7 @@ func (c *Checker) findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexIn } func (c *Checker) getIndexSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.getMembersOfSymbol(symbol)[ast.InternalSymbolNameIndex] + return c.getMembersOfSymbol(symbol).Get(ast.InternalSymbolNameIndex) } func (c *Checker) getSignaturesOfSymbol(symbol *ast.Symbol) []*Signature { @@ -21189,15 +22557,27 @@ func (c *Checker) instantiateSignatureEx(sig *Signature, m *TypeMapper, eraseTyp for _, tp := range freshTypeParameters { tp.AsTypeParameter().mapper = m } + markMapperEscaped(m) } // Don't compute resolvedReturnType and resolvedTypePredicate now, // because using `mapper` now could trigger inferences to become fixed. (See `createInferenceContext`.) // See GH#17600. - result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, freshTypeParameters, - c.instantiateSymbol(sig.ThisParameter(), m), c.instantiateSymbols(sig.parameters, m), + flags := sig.flags & (SignatureFlagsPropagatingFlags | signatureFlagsUninstantiatedParameters | signatureFlagsUninstantiatedThisParameter) + thisParam := sig.ThisParameter() + if m != nil { + if len(sig.parameters) != 0 { + flags |= signatureFlagsUninstantiatedParameters + } + if thisParam != nil { + flags |= signatureFlagsUninstantiatedThisParameter + } + } + result := c.newSignature(flags, sig.declaration, freshTypeParameters, + thisParam, sig.parameters, nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, int(sig.minArgumentCount)) result.target = sig result.mapper = m + markMapperEscaped(m) return result } @@ -21213,20 +22593,50 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { d := t.AsObjectType() if d.target != nil { c.setStructuredTypeMembers(t, nil, nil, nil, nil) - members := c.createInstantiatedSymbolTable(c.getPropertiesOfObjectType(d.target), d.mapper) + targetProps := c.getPropertiesOfObjectType(d.target) + targetMembers := d.target.AsStructuredType().getMembersIndex() + if (targetMembers == nil || targetMembers.fallback == nil) && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + var properties []*ast.Symbol + properties = c.instantiateSymbols(targetProps, d.mapper) + callSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindCall), d.mapper) + constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindConstruct), d.mapper) + indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(d.target), d.mapper) + data := t.AsStructuredType() + data.setMembersAndProperties(targetMembers, properties) + c.setStructuredTypeSignatures(data, callSignatures, constructSignatures, indexInfos) + return + } + members := c.createInstantiatedSymbolTable(targetProps, d.mapper) callSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindCall), d.mapper) constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindConstruct), d.mapper) indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(d.target), d.mapper) c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + c.releaseSymbolTableIfUnused(t, members) return } symbol := c.getMergedSymbol(t.symbol) if symbol.Flags()&ast.SymbolFlagsTypeLiteral != 0 { c.setStructuredTypeMembers(t, nil, nil, nil, nil) members := c.getMembersOfSymbol(symbol) - callSignatures := c.getSignaturesOfSymbol(members[ast.InternalSymbolNameCall]) - constructSignatures := c.getSignaturesOfSymbol(members[ast.InternalSymbolNameNew]) + callSymbol := members.Get(ast.InternalSymbolNameCall) + newSymbol := members.Get(ast.InternalSymbolNameNew) + callSignatures := c.getSignaturesOfSymbol(callSymbol) + constructSignatures := c.getSignaturesOfSymbol(newSymbol) indexInfos := c.getIndexInfosOfSymbol(symbol) + nonPropCount := 0 + if callSymbol != nil { + nonPropCount++ + } + if newSymbol != nil { + nonPropCount++ + } + if members.Get(ast.InternalSymbolNameIndex) != nil { + nonPropCount++ + } + if members.Len() == nonPropCount { + c.setStructuredTypeSignatures(t.AsStructuredType(), callSignatures, constructSignatures, indexInfos) + return + } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) return } @@ -21234,44 +22644,52 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { members := c.getExportsOfSymbol(symbol) var indexInfos []*IndexInfo if symbol == c.globalThisSymbol { - varsOnly := make(ast.SymbolTable) - for _, p := range members { + varsOnly := ast.NewSymbolTable() + for _, p := range members.All() { if p.Flags()&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags()&ast.SymbolFlagsValueModule != 0 && len(p.Declarations()) != 0 && core.Every(p.Declarations(), ast.IsAmbientModule)) { - varsOnly[p.Name()] = p + varsOnly.Set(p.Name(), p) } } members = varsOnly } var baseConstructorIndexInfo *IndexInfo c.setStructuredTypeMembers(t, members, nil, nil, nil) + ownsMembers := false if symbol.Flags()&ast.SymbolFlagsClass != 0 { classType := c.getDeclaredTypeOfClassOrInterface(symbol) baseConstructorType := c.getBaseConstructorTypeOfClass(classType) if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsTypeVariable) != 0 { - members = maps.Clone(members) - c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType)) + baseProps := c.getPropertiesOfType(baseConstructorType) + if len(baseProps) != 0 { + cloned := c.acquireSymbolTable() + cloned.CopyFrom(members) + members = c.addInheritedMembers(cloned, baseProps) + ownsMembers = true + } c.setStructuredTypeMembers(t, members, nil, nil, nil) } else if baseConstructorType == c.anyType { baseConstructorIndexInfo = c.anyBaseTypeIndexInfo } } - indexSymbol := members[ast.InternalSymbolNameIndex] + indexSymbol := members.Get(ast.InternalSymbolNameIndex) if indexSymbol != nil { - indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(members))) + indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(members.Values())) } else { if baseConstructorIndexInfo != nil { indexInfos = append(indexInfos, baseConstructorIndexInfo) } - if symbol.Flags()&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.getProperties(), func(prop *ast.Symbol) bool { + if symbol.Flags()&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.getPropertiesSlice(), func(prop *ast.Symbol) bool { return c.getTypeOfSymbol(prop).flags&TypeFlagsNumberLike != 0 })) { indexInfos = append(indexInfos, c.enumNumberIndexInfo) } } + if ownsMembers { + c.releaseSymbolTableIfUnused(t, members) + } if indexInfos != nil || d.extra != nil { d.ensureExtra().indexInfos = indexInfos } - // We resolve the members before computing the signatures because a signature may use // typeof with a qualified name expression that circularly references the type we are // in the process of resolving (see issue #6072). The temporarily empty signature list // will never be observed because a qualified name can't reference signatures. @@ -21286,7 +22704,7 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { // And likewise for construct signatures for classes if symbol.Flags()&ast.SymbolFlagsClass != 0 { classType := c.getDeclaredTypeOfClassOrInterface(symbol) - constructSignatures := c.getSignaturesOfSymbol(symbol.Members()[ast.InternalSymbolNameConstructor]) + constructSignatures := c.getSignaturesOfSymbol(symbol.Members().Get(ast.InternalSymbolNameConstructor)) if len(constructSignatures) == 0 { constructSignatures = c.getDefaultConstructSignatures(classType) } @@ -21301,59 +22719,75 @@ func (c *Checker) createInstantiatedSymbolTable(symbols []*ast.Symbol, m *TypeMa if len(symbols) == 0 { return nil } - result := make(ast.SymbolTable, len(symbols)) + result := c.acquireSymbolTable() for _, symbol := range symbols { - result[symbol.Name()] = c.instantiateSymbol(symbol, m) + result.Set(symbol.Name(), c.instantiateSymbol(symbol, m)) } return result } func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper) ast.SymbolTable { - if len(symbols) == 0 { + if symbols.Len() == 0 { return nil } - result := make(ast.SymbolTable, len(symbols)) - for id, symbol := range symbols { + result := c.acquireSymbolTable() + for id, symbol := range symbols.All() { if c.isNamedMember(symbol, id) { - result[id] = c.instantiateSymbol(symbol, m) + result.Set(id, c.instantiateSymbol(symbol, m)) } } return result } func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symbol { - if symbol == nil || m == nil { - return symbol + if symbol == nil { + return nil } - links := c.valueSymbolLinks.Get(symbol) - if m.MapsThisOnly() && isThisless(symbol) { + if m == nil || m.MapsThisOnly() && isThisless(symbol) || + symbol.Flags()&ast.SymbolFlagsFunctionScopedVariable != 0 && symbol.CheckFlags()&ast.CheckFlagsInstantiated == 0 && isTypeVariableFreeParameter(symbol) { + // If the type of the symbol is already resolved, and if that type could not possibly + // be affected by instantiation, simply return the symbol itself. return symbol } - // If the type of the symbol is already resolved, and if that type could not possibly - // be affected by instantiation, simply return the symbol itself. - if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { - if symbol.Flags()&ast.SymbolFlagsSetAccessor == 0 { - return symbol + links := c.valueSymbolLinks.TryGet(symbol) + if links != nil { + // If the type of the symbol is already resolved, and if that type could not possibly + // be affected by instantiation, simply return the symbol itself. + if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { + if symbol.Flags()&ast.SymbolFlagsSetAccessor == 0 { + return symbol + } + // If we're a setter, check writeType. + if links.getWriteType() != nil && !c.couldContainTypeVariables(links.getWriteType()) { + return symbol + } } - // If we're a setter, check writeType. - if links.getWriteType() != nil && !c.couldContainTypeVariables(links.getWriteType()) { - return symbol + if symbol.CheckFlags()&ast.CheckFlagsInstantiated != 0 { + // If symbol being instantiated is itself a instantiation, fetch the original target and combine the + // type mappers. This ensures that original type identities are properly preserved and that aliases + // always reference a non-aliases. + target := links.target + targetLinks := c.valueSymbolLinks.TryGet(target) + if targetLinks != nil && targetLinks.resolvedType != nil && !c.couldContainTypeVariables(targetLinks.resolvedType) { + if target.Flags()&ast.SymbolFlagsSetAccessor == 0 || (targetLinks.getWriteType() != nil && !c.couldContainTypeVariables(targetLinks.getWriteType())) { + return target + } + } + symbol = target + m = c.combineTypeMappers(links.mapper, m) + links = targetLinks } } - if symbol.CheckFlags()&ast.CheckFlagsInstantiated != 0 { - // If symbol being instantiated is itself a instantiation, fetch the original target and combine the - // type mappers. This ensures that original type identities are properly preserved and that aliases - // always reference a non-aliases. - symbol = links.target - m = c.combineTypeMappers(links.mapper, m) - } // Create a new transient symbol that shares the underlying data with the original symbol. result := c.newSharedDataSymbol(symbol) result.SetCheckFlags(ast.CheckFlagsInstantiated | symbol.CheckFlags()&(ast.CheckFlagsReadonly|ast.CheckFlagsLate|ast.CheckFlagsOptionalParameter|ast.CheckFlagsRestParameter)) resultLinks := c.valueSymbolLinks.Get(result) resultLinks.target = symbol resultLinks.mapper = m - resultLinks.setNameType(c, links.getNameType()) + markMapperEscaped(m) + if links != nil { + resultLinks.setNameType(c, links.getNameType()) + } return result } @@ -21393,26 +22827,65 @@ func isThislessVariableLikeDeclaration(node *ast.Node) bool { // literal type, an array with an element type that is free of this references, or a type reference that is // free of this references. func isThislessType(node *ast.Node) bool { - switch node.Kind { - case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword, - ast.KindSymbolKeyword, ast.KindObjectKeyword, ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword, ast.KindLiteralType: - return true - case ast.KindArrayType: - return isThislessType(node.AsArrayTypeNode().ElementType) - case ast.KindTypeReference: - return core.Every(node.TypeArguments(), isThislessType) + if node != nil { + switch node.Kind { + case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword, ast.KindSymbolKeyword, ast.KindObjectKeyword, ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNullKeyword, ast.KindNeverKeyword, ast.KindLiteralType: + return true + case ast.KindArrayType: + return isThislessType(node.AsArrayTypeNode().ElementType) + case ast.KindTypeReference: + return core.Every(node.TypeArguments(), isThislessType) + } } return false } -// A function-like declaration is considered free of `this` references if it has a return type -// annotation that is free of this references and if each parameter is thisless and if -// each type parameter (if present) is thisless. -func isThislessFunctionLikeDeclaration(node *ast.Node) bool { - returnType := node.Type() - return (ast.IsConstructorDeclaration(node) || returnType != nil && isThislessType(returnType)) && - core.Every(node.Parameters(), isThislessVariableLikeDeclaration) && - core.Every(node.TypeParameters(), isThislessTypeParameter) +func isTypeVariableFreeParameter(symbol *ast.Symbol) bool { + if decl := symbol.FirstDeclaration(); decl != nil && decl.Kind == ast.KindParameter && len(symbol.Declarations()) == 1 && !ast.IsInJSFile(decl) && decl.Initializer() == nil { + typeNode := decl.Type() + return typeNode != nil && typeNode.Flags&ast.NodeFlagsCondFlowClean != 0 && isTypeVariableFreeTypeNode(typeNode) + } + return false +} + +func isTypeVariableFreeTypeNode(node *ast.Node) bool { + if node != nil { + switch node.Kind { + case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword, ast.KindSymbolKeyword, ast.KindObjectKeyword, ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNullKeyword, ast.KindNeverKeyword: + return true + case ast.KindLiteralType: + lit := node.AsLiteralTypeNode().Literal + return lit != nil && lit.Kind != ast.KindTemplateExpression + case ast.KindArrayType: + return isTypeVariableFreeTypeNode(node.AsArrayTypeNode().ElementType) + case ast.KindParenthesizedType: + return isTypeVariableFreeTypeNode(node.AsParenthesizedTypeNode().Type) + case ast.KindTypeOperator: + return node.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword && isTypeVariableFreeTypeNode(node.AsTypeOperatorNode().Type) + case ast.KindUnionType: + return core.Every(node.AsUnionTypeNode().Types.Nodes, isTypeVariableFreeTypeNode) + case ast.KindIntersectionType: + return core.Every(node.AsIntersectionTypeNode().Types.Nodes, isTypeVariableFreeTypeNode) + case ast.KindTupleType: + return core.Every(node.AsTupleTypeNode().Elements.Nodes, isTypeVariableFreeTypeNode) + case ast.KindNamedTupleMember: + return isTypeVariableFreeTypeNode(node.AsNamedTupleMember().Type) + case ast.KindRestType: + return isTypeVariableFreeTypeNode(node.AsRestTypeNode().Type) + case ast.KindOptionalType: + return isTypeVariableFreeTypeNode(node.AsOptionalTypeNode().Type) + } + } + return false +} + +// annotation that is free of this references and if each parameter is thisless and if +// each type parameter (if present) is thisless. +func isThislessFunctionLikeDeclaration(node *ast.Node) bool { + returnType := node.Type() + return (ast.IsConstructorDeclaration(node) || returnType != nil && isThislessType(returnType)) && + core.Every(node.Parameters(), isThislessVariableLikeDeclaration) && + core.Every(node.TypeParameters(), isThislessTypeParameter) } // A type parameter is thisless if its constraint is thisless, or if it has no constraint. */ @@ -21458,9 +22931,138 @@ func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature { return result } +func (c *Checker) getLazyMappedTable(t *Type) *lazyMappedTable { + if t.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) != ObjectFlagsMapped { + return nil + } + m := t.AsMappedType() + if m.lazy != nil { + return m.lazy + } + if !c.isMappedTypeWithKeyofConstraintDeclaration(t) { + return nil + } + typeParameter := c.getTypeParameterFromMappedType(t) + c.getConstraintTypeFromMappedType(t) + mappedType := core.OrElse(m.target, t) + if c.getNameTypeFromMappedType(mappedType) != nil { + return nil + } + shouldLinkPropDeclarations := c.getMappedTypeNameTypeKind(mappedType) != MappedTypeNameTypeKindRemapping + modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) + if modifiersType.flags&TypeFlagsObject == 0 || t.objectFlags&ObjectFlagsMembersResolved != 0 { + return nil + } + lazy := c.lazyMappedTableArena.New() + lazy.typeParameter = typeParameter + lazy.modifiersType = modifiersType + lazy.templateModifiers = getMappedTypeModifiers(t) + lazy.shouldLinkPropDeclarations = shouldLinkPropDeclarations + m.lazy = lazy + return lazy +} + +func (c *Checker) getLazyMappedTypeMember(t *Type, lazy *lazyMappedTable, name string) (*ast.Symbol, bool) { + if lazy.members != nil { + if existing, ok := lazy.members[name]; ok { + return existing, true + } + } else { + lazy.members = make(map[string]*ast.Symbol) + } + // Recursive lookups see nil, as they would while resolveMappedTypeMembers runs. + lazy.members[name] = nil + modifiersProp := c.getMemberOfStructuredType(lazy.modifiersType, name) + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return t.AsStructuredType().getMember(name), true + } + var member *ast.Symbol + if modifiersProp != nil && c.isNamedMember(modifiersProp, name) { + keyType := c.getLiteralTypeFromProperty(modifiersProp, TypeFlagsStringOrNumberLiteralOrUnique, false) + if !isTypeUsableAsPropertyName(keyType) || getPropertyNameFromType(keyType) != name { + delete(lazy.members, name) + return nil, false + } + member = c.newMappedTypeMemberWithProp(t, modifiersProp, lazy.templateModifiers, lazy.shouldLinkPropDeclarations, keyType, keyType, name) + } + lazy.members[name] = member + return member, true +} + +func (c *Checker) getLazyMappedTypeIndexInfos(t *Type, lazy *lazyMappedTable) []*IndexInfo { + if !lazy.indexInfosReady { + if lazy.resolving { + return nil + } + lazy.resolving = true + templateType := c.getTemplateTypeFromMappedType(core.OrElse(t.AsMappedType().target, t)) + var indexBuf [4]*IndexInfo + indexInfos := indexBuf[:0] + for _, info := range c.getIndexInfosOfType(lazy.modifiersType) { + indexInfos = c.appendMappedTypeIndexInfo(indexInfos, t, lazy.modifiersType, lazy.typeParameter, templateType, lazy.templateModifiers, info.keyType, info.keyType) + } + lazy.resolving = false + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return t.AsStructuredType().getIndexInfos() + } + if len(indexInfos) != 0 { + lazy.indexInfos = c.indexInfoListArena.Clone(indexInfos) + } + lazy.indexInfosReady = true + } + return lazy.indexInfos +} + +func (c *Checker) newMappedTypeMember(t *Type, modifiersType *Type, templateModifiers MappedTypeModifiers, shouldLinkPropDeclarations bool, keyType *Type, propNameType *Type, propName string) *ast.Symbol { + var modifiersProp *ast.Symbol + if isTypeUsableAsPropertyName(keyType) { + modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) + } + return c.newMappedTypeMemberWithProp(t, modifiersProp, templateModifiers, shouldLinkPropDeclarations, keyType, propNameType, propName) +} + +func (c *Checker) newMappedTypeMemberWithProp(t *Type, modifiersProp *ast.Symbol, templateModifiers MappedTypeModifiers, shouldLinkPropDeclarations bool, keyType *Type, propNameType *Type, propName string) *ast.Symbol { + isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags()&ast.SymbolFlagsOptional != 0 + isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp) + stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags()&ast.SymbolFlagsOptional != 0 + var lateFlag ast.CheckFlags + if modifiersProp != nil { + lateFlag = modifiersProp.CheckFlags() & ast.CheckFlagsLate + } + prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName) + prop.SetCheckFlags(lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0)) + valueLinks := c.valueSymbolLinks.Get(prop) + valueLinks.setMappedInfo(c, t, propNameType, keyType, modifiersProp) + if modifiersProp != nil && shouldLinkPropDeclarations { + prop.SetDeclarations(modifiersProp.Declarations()) + } + return prop +} + +func (c *Checker) appendMappedTypeIndexInfo(indexInfos []*IndexInfo, t *Type, modifiersType *Type, typeParameter *Type, templateType *Type, templateModifiers MappedTypeModifiers, keyType *Type, propNameType *Type) []*IndexInfo { + if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 { + indexKeyType := propNameType + switch { + case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0: + indexKeyType = c.stringType + case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: + indexKeyType = c.numberType + } + propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) + modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType) + isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly + indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil) + indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/) + } + return indexInfos +} + func (c *Checker) resolveMappedTypeMembers(t *Type) { - members := make(ast.SymbolTable) - var indexInfos []*IndexInfo + lazy := t.AsMappedType().lazy + t.AsMappedType().lazy = nil + members := c.acquireSymbolTable() + var indexBuf [4]*IndexInfo + indexInfos := indexBuf[:0] // Resolve upfront such that recursive references see an empty object type. c.setStructuredTypeMembers(t, nil, nil, nil, nil) // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, @@ -21471,8 +23073,7 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { nameType := c.getNameTypeFromMappedType(mappedType) shouldLinkPropDeclarations := c.getMappedTypeNameTypeKind(mappedType) != MappedTypeNameTypeKindRemapping templateType := c.getTemplateTypeFromMappedType(mappedType) - modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) - // The 'T' in 'keyof T' + modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) // The 'T' in 'keyof T' templateModifiers := getMappedTypeModifiers(t) include := TypeFlagsStringOrNumberLiteralOrUnique addMemberForKeyTypeWorker := func(keyType *Type, propNameType *Type) { @@ -21483,51 +23084,17 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { // String enum members from separate enums with identical values // are distinct types with the same property name. Make the resulting // property symbol's name type be the union of those enum member types. - if existingProp := members[propName]; existingProp != nil { + if existingProp := members.Get(propName); existingProp != nil { valueLinks := c.valueSymbolLinks.Get(existingProp) valueLinks.setNameType(c, c.getUnionType([]*Type{valueLinks.getNameType(), propNameType})) - mappedLinks := c.mappedSymbolLinks.Get(existingProp) - mappedLinks.keyType = c.getUnionType([]*Type{mappedLinks.keyType, keyType}) + valueLinks.setWriteType(c, c.getUnionType([]*Type{valueLinks.getWriteType(), keyType})) + } else if lazy != nil && lazy.members != nil && lazy.members[propName] != nil { + members.Set(propName, lazy.members[propName]) } else { - var modifiersProp *ast.Symbol - if isTypeUsableAsPropertyName(keyType) { - modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) - } - isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags()&ast.SymbolFlagsOptional != 0 - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp) - stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags()&ast.SymbolFlagsOptional != 0 - var lateFlag ast.CheckFlags - if modifiersProp != nil { - lateFlag = modifiersProp.CheckFlags() & ast.CheckFlagsLate - } - prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName) - prop.SetCheckFlags(lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0)) - valueLinks := c.valueSymbolLinks.Get(prop) - valueLinks.setContainingType(c, t) - valueLinks.setNameType(c, propNameType) - mappedLinks := c.mappedSymbolLinks.Get(prop) - mappedLinks.keyType = keyType - if modifiersProp != nil { - mappedLinks.syntheticOrigin = modifiersProp - if shouldLinkPropDeclarations { - prop.SetDeclarations(modifiersProp.Declarations()) - } - } - members[propName] = prop - } - } else if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 { - indexKeyType := propNameType - switch { - case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0: - indexKeyType = c.stringType - case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: - indexKeyType = c.numberType + members.Set(propName, c.newMappedTypeMember(t, modifiersType, templateModifiers, shouldLinkPropDeclarations, keyType, propNameType, propName)) } - propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) - modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType) - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly - indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil) - indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/) + } else if lazy == nil || !lazy.indexInfosReady { + indexInfos = c.appendMappedTypeIndexInfo(indexInfos, t, modifiersType, typeParameter, templateType, templateModifiers, keyType, propNameType) } } addMemberForKeyType := func(keyType *Type) { @@ -21535,17 +23102,38 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { if nameType != nil { propNameType = c.instantiateType(nameType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) } - forEachType(propNameType, func(t *Type) { - addMemberForKeyTypeWorker(keyType, t) - }) + if propNameType.flags&TypeFlagsUnion != 0 { + for _, pt := range propNameType.Types() { + addMemberForKeyTypeWorker(keyType, pt) + } + } else if propNameType.flags&TypeFlagsNever == 0 { + addMemberForKeyTypeWorker(keyType, propNameType) + } } if c.isMappedTypeWithKeyofConstraintDeclaration(t) { // We have a { [P in keyof T]: X } c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, false /*stringsOnly*/, addMemberForKeyType) } else { - forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType) + bound := c.getLowerBoundOfKeyType(constraintType) + if bound.flags&TypeFlagsUnion != 0 { + for _, kt := range bound.Types() { + addMemberForKeyType(kt) + } + } else if bound.flags&TypeFlagsNever == 0 { + addMemberForKeyType(bound) + } + } + var finalIndexInfos []*IndexInfo + if lazy != nil && lazy.indexInfosReady { + finalIndexInfos = lazy.indexInfos + } else if len(indexInfos) != 0 { + finalIndexInfos = c.indexInfoListArena.Clone(indexInfos) + } + c.setStructuredTypeMembers(t, members, nil, nil, finalIndexInfos) + c.releaseSymbolTableIfUnused(t, members) + if lazy != nil { + lazy.members = nil } - c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) } func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { @@ -21557,7 +23145,7 @@ func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { return c.errorType } templateType := c.getTemplateTypeFromMappedType(core.OrElse(mappedType.AsMappedType().target, mappedType)) - mapper := appendTypeMapping(mappedType.AsMappedType().mapper, c.getTypeParameterFromMappedType(mappedType), c.mappedSymbolLinks.Get(symbol).keyType) + mapper := appendTypeMapping(mappedType.AsMappedType().mapper, c.getTypeParameterFromMappedType(mappedType), links.getWriteType()) propType := c.instantiateType(templateType, mapper) // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks @@ -21632,7 +23220,8 @@ func (c *Checker) resolveUnionTypeMembers(t *Type) { return c.getSignaturesOfType(t, SignatureKindConstruct) })) indexInfos := c.getUnionIndexInfos(t.Types()) - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) + t.objectFlags |= ObjectFlagsMembersResolved + c.setStructuredTypeSignatures(t.AsStructuredType(), callSignatures, constructSignatures, indexInfos) } func (c *Checker) getArrayMemberCallSignatures(t *Type) []*Signature { @@ -21696,16 +23285,13 @@ func (c *Checker) getUnionSignatures(signatureLists [][]*Signature) []*Signature s := signature // Union the result types when more than one signature matches if len(unionSignatures) > 1 { - thisParameter := signature.ThisParameter() + thisParameter := c.getSignatureThisParameter(signature) firstThisParameterOfUnionSignatures := core.FirstNonNil(unionSignatures, func(sig *Signature) *ast.Symbol { return sig.ThisParameter() }) if firstThisParameterOfUnionSignatures != nil { thisType := c.getIntersectionType(core.MapNonNil(unionSignatures, func(sig *Signature) *Type { - if sig.ThisParameter() != nil { - return c.getTypeOfSymbol(sig.ThisParameter()) - } - return nil + return c.getThisTypeOfSignature(sig) })) thisParameter = c.createSymbolWithType(firstThisParameterOfUnionSignatures, thisType) } @@ -21764,7 +23350,7 @@ func (c *Checker) combineUnionOrIntersectionMemberSignatures(left *Signature, ri if lastParam != nil && lastParam.CheckFlags()&ast.CheckFlagsRestParameter != 0 { flags |= SignatureFlagsHasRestParameter } - thisParam := c.combineUnionOrIntersectionThisParam(left.ThisParameter(), right.ThisParameter(), paramMapper, isUnion) + thisParam := c.combineUnionOrIntersectionThisParam(c.getSignatureThisParameter(left), c.getSignatureThisParameter(right), paramMapper, isUnion) minArgCount := int(max(left.minArgumentCount, right.minArgumentCount)) result := c.newSignature(flags, declaration, typeParams, thisParam, params, nil, nil, minArgCount) var leftSignatures []*Signature @@ -21780,8 +23366,10 @@ func (c *Checker) combineUnionOrIntersectionMemberSignatures(left *Signature, ri } else { result.mapper = paramMapper } + markMapperEscaped(result.mapper) } else if left.getComposite() != nil && left.getComposite().isUnion == isUnion { result.mapper = left.mapper + markMapperEscaped(result.mapper) } return result } @@ -21869,19 +23457,51 @@ func (c *Checker) combineUnionOrIntersectionThisParam(left *ast.Symbol, right *a func (c *Checker) resolveIntersectionTypeMembers(t *Type) { // The members and properties collections are empty for intersection types. To get all properties of an // intersection type use getPropertiesOfType (only the language service uses this). - var callSignatures []*Signature - var constructSignatures []*Signature - var indexInfos []*IndexInfo + var callBuf [4]*Signature + var constructBuf [4]*Signature + var indexBuf [4]*IndexInfo + callSignatures := callBuf[:0] + constructSignatures := constructBuf[:0] + indexInfos := indexBuf[:0] types := t.Types() - mixinFlags, mixinCount := c.findMixins(types) - for i, t := range types { + var mixinFlagsBuf [8]bool + var mixinFlags []bool + var mixinCount int + constructorTypeCount := 0 + for _, ct := range types { + if len(c.getSignaturesOfType(ct, SignatureKindConstruct)) > 0 { + constructorTypeCount++ + } + } + if constructorTypeCount >= 2 { + if len(types) <= len(mixinFlagsBuf) { + mixinFlags = mixinFlagsBuf[:len(types)] + } else { + mixinFlags = make([]bool, len(types)) + } + firstMixinIndex := -1 + for i, ct := range types { + if c.isMixinConstructorType(ct) { + mixinFlags[i] = true + if firstMixinIndex < 0 { + firstMixinIndex = i + } + mixinCount++ + } + } + if constructorTypeCount == mixinCount { + mixinFlags[firstMixinIndex] = false + mixinCount-- + } + } + for i, ct := range types { // When an intersection type contains mixin constructor types, the construct signatures from // those types are discarded and their return types are mixed into the return types of all // other construct signatures in the intersection type. For example, the intersection type // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature // 'new(s: string) => A & B'. - if !mixinFlags[i] { - signatures := c.getSignaturesOfType(t, SignatureKindConstruct) + if mixinFlags == nil || !mixinFlags[i] { + signatures := c.getSignaturesOfType(ct, SignatureKindConstruct) if len(signatures) != 0 && mixinCount > 0 { signatures = core.Map(signatures, func(s *Signature) *Signature { clone := c.cloneSignature(s) @@ -21891,12 +23511,24 @@ func (c *Checker) resolveIntersectionTypeMembers(t *Type) { } constructSignatures = c.appendSignatures(constructSignatures, signatures) } - callSignatures = c.appendSignatures(callSignatures, c.getSignaturesOfType(t, SignatureKindCall)) - for _, info := range c.getIndexInfosOfType(t) { + callSignatures = c.appendSignatures(callSignatures, c.getSignaturesOfType(ct, SignatureKindCall)) + for _, info := range c.getIndexInfosOfType(ct) { indexInfos = c.appendIndexInfo(indexInfos, info, false /*union*/) } } - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) + var finalCalls, finalConstructs []*Signature + var finalIndexInfos []*IndexInfo + if len(callSignatures) != 0 { + finalCalls = c.signatureListArena.Clone(callSignatures) + } + if len(constructSignatures) != 0 { + finalConstructs = c.signatureListArena.Clone(constructSignatures) + } + if len(indexInfos) != 0 { + finalIndexInfos = c.indexInfoListArena.Clone(indexInfos) + } + t.objectFlags |= ObjectFlagsMembersResolved + c.setStructuredTypeSignatures(t.AsStructuredType(), finalCalls, finalConstructs, finalIndexInfos) } func (c *Checker) appendSignatures(signatures []*Signature, newSignatures []*Signature) []*Signature { @@ -21969,8 +23601,7 @@ func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, in */ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.getMember(name) + symbol := c.getMemberOfStructuredType(t, name) if symbol != nil && c.symbolIsValue(symbol) { return symbol } @@ -21993,52 +23624,134 @@ func (c *Checker) getPropertyOfUnionOrIntersectionType(t *Type, name string, ski // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { - var cache ast.SymbolTable + data := t.AsUnionOrIntersectionType() if skipObjectFunctionPropertyAugment { - cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment) + if prop := data.propertyCacheWithoutFunctionPropertyAugment.Get(name); prop != nil { + return prop + } } else { - cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache) - } - if prop := cache[name]; prop != nil { - return prop + if data.propertyCache != nil { + if prop := data.propertyCache.Get(name); prop != nil { + return prop + } + } + // Complete non-augmented properties are also valid for augmented + // lookup. Share the entry instead of retaining it in both tables. + if prop := data.propertyCacheWithoutFunctionPropertyAugment.Get(name); prop != nil && prop.CheckFlags()&ast.CheckFlagsPartial == 0 { + return prop + } } prop := c.createUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment) if prop != nil { - cache[name] = prop - // Propagate an entry from the non-augmented cache to the augmented cache unless the property is partial. - if skipObjectFunctionPropertyAugment && prop.CheckFlags()&ast.CheckFlagsPartial == 0 { - augmentedCache := ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache) - if augmentedCache[name] == nil { - augmentedCache[name] = prop + if skipObjectFunctionPropertyAugment { + ast.GetSymbolTable(&data.propertyCacheWithoutFunctionPropertyAugment).Set(name, prop) + if prop.CheckFlags()&ast.CheckFlagsPartial == 0 && data.propertyCache.Get(name) == prop { + data.propertyCache.Delete(name) + if data.propertyCache.Len() == 0 { + data.propertyCache = nil + } } + } else { + ast.GetSymbolTable(&data.propertyCache).Set(name, prop) } } return prop } -func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { +// unionOrIntersectionPropertySources describes the constituent properties from +// which a union or intersection property would be synthesized. +type unionOrIntersectionPropertySources struct { + singleProp *ast.Symbol + // props holds distinct constituent properties in first-seen order, or is + // empty when every constituent shares singleProp. propsByKey indexes it + // only once it is large enough for hashing to beat a linear scan. + props []*ast.Symbol + propsByKey map[*ast.Symbol]struct{} + indexTypes []*Type + propFlags ast.SymbolFlags + optionalFlag ast.SymbolFlags + checkFlags ast.CheckFlags + syntheticFlag ast.CheckFlags + mergedInstantiations bool +} + +// isSynthetic reports whether the sources require a new synthetic symbol rather +// than reusing (or cloning) singleProp. +func (s *unionOrIntersectionPropertySources) isSynthetic() bool { + return len(s.props) != 0 || s.checkFlags&ast.CheckFlagsReadPartial != 0 || len(s.indexTypes) != 0 +} + +// addProp adds prop to props if it is not already present, preserving +// first-insertion order. +func (s *unionOrIntersectionPropertySources) addProp(c *Checker, prop *ast.Symbol) { + if s.propsByKey == nil { + if slices.Contains(s.props, prop) { + return + } + if s.props == nil { + s.props = c.getTemporarySymbolList() + } + s.props = append(s.props, prop) + if len(s.props) > orderedSetMapThreshold { + s.propsByKey = make(map[*ast.Symbol]struct{}, len(s.props)) + for _, p := range s.props { + s.propsByKey[p] = struct{}{} + } + } + return + } + if _, ok := s.propsByKey[prop]; ok { + return + } + s.props = append(s.props, prop) + s.propsByKey[prop] = struct{}{} +} + +func (c *Checker) getUnionOrIntersectionPropertySources(containingType *Type, name string, skipObjectFunctionPropertyAugment bool, sources *unionOrIntersectionPropertySources) bool { propFlags := ast.SymbolFlagsNone - var singleProp *ast.Symbol - var propSet collections.OrderedSet[*ast.Symbol] - var indexTypes []*Type isUnion := containingType.flags&TypeFlagsUnion != 0 // Flags we want to propagate to the result if they exist in all source symbols var checkFlags ast.CheckFlags var optionalFlag ast.SymbolFlags + var singleProp *ast.Symbol + var indexTypes []*Type if !isUnion { checkFlags = ast.CheckFlagsReadonly optionalFlag = ast.SymbolFlagsOptional } syntheticFlag := ast.CheckFlagsSyntheticMethod mergedInstantiations := false + nameHash := hashMemberName(name) + mayBeGlobal := !skipObjectFunctionPropertyAugment && c.mayBeGlobalObjectMember(name) for _, current := range containingType.Types() { - t := c.getApparentType(current) - if !c.isErrorType(t) && t.flags&TypeFlagsNever == 0 { - prop := c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false) + var t *Type + var prop *ast.Symbol + if current.flags == TypeFlagsObject && current.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) == ObjectFlagsMembersResolved && (current.symbol == nil || current.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + t = current + if sym := current.AsStructuredType().getMemberWithHash(name, nameHash); sym != nil { + if sym.Flags()&ast.SymbolFlagsValue != 0 { + prop = sym + } else { + prop = c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false /*includeTypeOnlyMembers*/) + } + } else if mayBeGlobal { + prop = c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false /*includeTypeOnlyMembers*/) + } + } else { + t = c.getApparentType(current) + if c.isErrorType(t) || t.flags&TypeFlagsNever != 0 { + continue + } + prop = c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false /*includeTypeOnlyMembers*/) + } + if true { var modifiers ast.ModifierFlags if prop != nil { modifiers = getDeclarationModifierFlagsFromSymbol(prop) - writeModifiers := getDeclarationModifierFlagsFromSymbolEx(prop, true /*isWrite*/) + writeModifiers := modifiers + if prop.Flags()&ast.SymbolFlagsSetAccessor != 0 || prop.CheckFlags()&ast.CheckFlagsSynthetic != 0 { + writeModifiers = getDeclarationModifierFlagsFromSymbolEx(prop, true /*isWrite*/) + } if prop.Flags()&ast.SymbolFlagsClassMember != 0 { if isUnion { optionalFlag |= prop.Flags() & ast.SymbolFlagsOptional @@ -22060,10 +23773,10 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s // back and not `Array.length` when we're looking at a `.length` access on a `string[] | number[]` mergedInstantiations = singleProp.Parent() != nil && len(c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.Parent())) != 0 } else { - if propSet.Size() == 0 { - propSet.Add(singleProp) + if len(sources.props) == 0 { + sources.addProp(c, singleProp) } - propSet.Add(prop) + sources.addProp(c, prop) } // classes created by mixins are represented as intersections // and overriding a property in a derived class redefines it completely at runtime @@ -22111,12 +23824,21 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s if indexType == nil { indexType = c.undefinedType } + if indexTypes == nil { + indexTypes = c.getTemporaryTypeArguments(0)[:0] + } indexTypes = append(indexTypes, indexType) } else { + if indexTypes == nil { + indexTypes = c.getTemporaryTypeArguments(0)[:0] + } indexTypes = append(indexTypes, indexInfo.valueType) } } else if isObjectLiteralType(t) && t.objectFlags&ObjectFlagsContainsSpread == 0 { checkFlags |= ast.CheckFlagsWritePartial + if indexTypes == nil { + indexTypes = c.getTemporaryTypeArguments(0)[:0] + } indexTypes = append(indexTypes, c.undefinedType) } else { checkFlags |= ast.CheckFlagsReadPartial @@ -22125,18 +23847,26 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s } } if singleProp == nil { + c.releaseTemporarySymbolList(sources.props) + if indexTypes != nil { + c.releaseTemporaryTypeArguments(indexTypes) + } // No property was found - return nil + return false } if isUnion && - (propSet.Size() != 0 || checkFlags&ast.CheckFlagsPartial != 0) && + (len(sources.props) != 0 || checkFlags&ast.CheckFlagsPartial != 0) && checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected|ast.CheckFlagsContainsWritePrivate|ast.CheckFlagsContainsWriteProtected) != 0 && - !(propSet.Size() != 0 && c.hasCommonDeclaration(&propSet)) { + !(len(sources.props) != 0 && c.hasCommonDeclaration(sources.props)) { // A property in a union has a private or protected declaration in one constituent, but is missing // or has a different declaration in another constituent. If the private or protected declaration is // for reading, we don't create a property. if checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected) != 0 { - return nil + c.releaseTemporarySymbolList(sources.props) + if indexTypes != nil { + c.releaseTemporaryTypeArguments(indexTypes) + } + return false } // Otherwise, if the private or protected declaration is for writing, reduce accessibility to that of // the most restricted constituent. @@ -22146,8 +23876,36 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s checkFlags &^= ast.CheckFlagsContainsWritePublic } } - if propSet.Size() == 0 && checkFlags&ast.CheckFlagsReadPartial == 0 && len(indexTypes) == 0 { - if !mergedInstantiations { + sources.singleProp = singleProp + sources.indexTypes = indexTypes + sources.propFlags = propFlags + sources.optionalFlag = optionalFlag + sources.checkFlags = checkFlags + sources.syntheticFlag = syntheticFlag + sources.mergedInstantiations = mergedInstantiations + return true +} + +func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { + var sources unionOrIntersectionPropertySources + if !c.getUnionOrIntersectionPropertySources(containingType, name, skipObjectFunctionPropertyAugment, &sources) { + return nil + } + defer func() { + c.releaseTemporarySymbolList(sources.props) + if sources.indexTypes != nil { + c.releaseTemporaryTypeArguments(sources.indexTypes) + } + }() + singleProp := sources.singleProp + indexTypes := sources.indexTypes + propFlags := sources.propFlags + optionalFlag := sources.optionalFlag + checkFlags := sources.checkFlags + syntheticFlag := sources.syntheticFlag + isUnion := containingType.flags&TypeFlagsUnion != 0 + if !sources.isSynthetic() { + if !sources.mergedInstantiations { return singleProp } // No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents) @@ -22170,17 +23928,22 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s links.setWriteType(c, c.getWriteTypeOfSymbol(singleProp)) return clone } - if propSet.Size() == 0 { - propSet.Add(singleProp) + props := sources.props + var singlePropBuf [1]*ast.Symbol + if len(props) == 0 { + singlePropBuf[0] = sources.singleProp + props = singlePropBuf[:] } - var declarations []*ast.Node + var declarationsBuf [8]*ast.Node + declarations := declarationsBuf[:0] var firstType *Type var nameType *Type - var propTypes []*Type + propTypes := c.getTemporaryTypeArguments(0)[:0] + defer c.releaseTemporaryTypeArguments(propTypes) var writeTypes []*Type var firstValueDeclaration *ast.Node var hasNonUniformValueDeclaration bool - for prop := range propSet.Values() { + for _, prop := range props { if firstValueDeclaration == nil { firstValueDeclaration = prop.ValueDeclaration() } else if prop.ValueDeclaration() != nil && prop.ValueDeclaration() != firstValueDeclaration { @@ -22192,7 +23955,7 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s t := c.getTypeOfSymbol(prop) if firstType == nil { firstType = t - nameType = c.valueSymbolLinks.Get(prop).getNameType() + nameType = c.valueSymbolLinks.TryGet(prop).getNameType() } writeType := c.getWriteTypeOfSymbol(prop) if writeTypes != nil || writeType != t { @@ -22214,7 +23977,11 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s } propTypes = append(propTypes, indexTypes...) result := c.newSymbolEx(propFlags|optionalFlag, name, checkFlags|syntheticFlag) - result.SetDeclarations(declarations) + if len(declarations) == 1 { + result.SetSingleDeclaration(declarations[0]) + } else if len(declarations) > 1 { + result.SetDeclarations(slices.Clone(declarations)) + } if !hasNonUniformValueDeclaration && firstValueDeclaration != nil { result.SetValueDeclaration(firstValueDeclaration) // Inherit information about parent type. @@ -22228,7 +23995,7 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s result.SetCheckFlags(result.CheckFlags() | ast.CheckFlagsDeferredType) deferred := c.deferredSymbolLinks.Get(result) deferred.parent = containingType - deferred.constituents = propTypes + deferred.constituents = c.typeListArena.Clone(propTypes) deferred.writeConstituents = writeTypes return result } @@ -22251,7 +24018,10 @@ func (c *Checker) getTargetSymbol(s *ast.Symbol) *ast.Symbol { // if symbol is instantiated its flags are not copied from the 'target' // so we'll need to get back original 'target' symbol to work with correct set of flags if s != nil && s.CheckFlags()&ast.CheckFlagsInstantiated != 0 { - return c.valueSymbolLinks.Get(s).target + if links := c.valueSymbolLinks.TryGet(s); links != nil { + return links.target + } + return nil } return s } @@ -22264,9 +24034,9 @@ func isPrototypeProperty(symbol *ast.Symbol) bool { return symbol.Flags()&ast.SymbolFlagsMethod != 0 || symbol.CheckFlags()&ast.CheckFlagsSyntheticMethod != 0 } -func (c *Checker) hasCommonDeclaration(symbols *collections.OrderedSet[*ast.Symbol]) bool { +func (c *Checker) hasCommonDeclaration(symbols []*ast.Symbol) bool { var commonDeclarations collections.Set[*ast.Node] - for symbol := range symbols.Values() { + for _, symbol := range symbols { if len(symbol.Declarations()) == 0 { return false } @@ -22289,14 +24059,12 @@ func (c *Checker) hasCommonDeclaration(symbols *collections.OrderedSet[*ast.Symb } func (c *Checker) createSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol { - symbol := c.newSymbolEx(source.Flags(), source.Name(), source.CheckFlags()&ast.CheckFlagsReadonly) - symbol.SetDeclarations(source.Declarations()) - symbol.SetParent(source.Parent()) - symbol.SetValueDeclaration(source.ValueDeclaration()) + symbol := c.newSharedDataSymbol(source) + symbol.SetCheckFlags(source.CheckFlags() & ast.CheckFlagsReadonly) links := c.valueSymbolLinks.Get(symbol) links.resolvedType = t links.target = source - links.setNameType(c, c.valueSymbolLinks.Get(source).getNameType()) + links.setNameType(c, c.valueSymbolLinks.TryGet(source).getNameType()) return symbol } @@ -22315,6 +24083,13 @@ func (c *Checker) isMappedTypeGenericIndexedAccess(t *Type) bool { * type itself. */ func (c *Checker) getApparentType(t *Type) *Type { + if t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped == 0 { + return t + } + return c.getApparentTypeSlow(t) +} + +func (c *Checker) getApparentTypeSlow(t *Type) *Type { originalType := t if t.flags&TypeFlagsInstantiable != 0 { t = c.getBaseConstraintOfType(t) @@ -22405,6 +24180,13 @@ func (c *Checker) getApparentTypeOfIntersectionType(t *Type, thisArgument *Type) * no constituent property has type 'never', but the intersection of the constituent property types is 'never'. */ func (c *Checker) getReducedType(t *Type) *Type { + if t.flags&(TypeFlagsIntersection|TypeFlags(t.objectFlags&ObjectFlagsContainsIntersections)<<2) == 0 { + return t + } + return c.getReducedTypeSlow(t) +} + +func (c *Checker) getReducedTypeSlow(t *Type) *Type { switch { case t.flags&TypeFlagsUnion != 0: if t.objectFlags&ObjectFlagsContainsIntersections != 0 { @@ -22445,18 +24227,48 @@ func (c *Checker) isMappingOfSameObjectType(types []*Type) bool { func (c *Checker) somePropertyReducesToNever(t *Type) bool { // Collect declaration counts for each property across all constituent types of the intersection. + // A name that occurs in more than one constituent occurs in one other than skipped, so skipped + // only needs its members looked up, which may not resolve them. + types := t.Types() + skipped := -1 + for i, ct := range types { + if ct.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) == ObjectFlagsMapped || ct.flags&TypeFlagsObject != 0 && ct.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + skipped = i + break + } + } + if len(types) == 2 && skipped != -1 { + skippedType := types[skipped] + otherProps, _ := c.getPropertiesOfTypeLazily(types[1-skipped]) + for _, prop := range otherProps { + propName := prop.Name() + if c.getPropertyOfTypeWorker(skippedType, propName, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/, false /*instantiate*/) != nil { + if c.intersectionPropertyReducesToNever(t, propName) { + return true + } + } + } + return false + } counts := c.getTemporaryPropertyCounts() defer c.releaseTemporaryPropertyCounts(counts) - for _, t := range t.Types() { - for _, prop := range c.getLayoutPropertiesOfType(t) { - counts.Set(prop.Name(), counts.GetOrZero(prop.Name())+1) + // Property names are unique within each constituent, so the first + // constituent's names can be added without looking them up. + firstProps, _ := c.getPropertiesOfTypeLazily(types[0]) + for _, prop := range firstProps { + counts.AddNew(prop.Name()) + } + for _, ct := range types[1:] { + props, _ := c.getPropertiesOfTypeLazily(ct) + for _, prop := range props { + counts.Add(prop.Name()) } } // Check if any property appears in more than one constituent type and reduces to 'never'. // Go in the order the properties were found so the combined properties are created in the same order every time. - for propName, count := range counts.Entries() { - if count > 1 { - if prop := c.getPropertyOfUnionOrIntersectionType(t, propName, true /*skipObjectFunctionPropertyAugment*/); prop != nil && c.isNeverReducedProperty(prop) { + for i := range counts.entries { + if e := counts.entries[i]; e.count > 1 { + if prop := c.getPropertyOfUnionOrIntersectionType(t, e.name, true /*skipObjectFunctionPropertyAugment*/); prop != nil && c.isNeverReducedProperty(prop) { return true } } @@ -22464,6 +24276,74 @@ func (c *Checker) somePropertyReducesToNever(t *Type) bool { return false } +// intersectionPropertyReducesToNever reports whether the combined property named +// name would satisfy isNeverReducedProperty, without creating and caching that +// property. Most such properties are never otherwise looked up, so creating +// them only to inspect their flags retains a large number of symbols. +func (c *Checker) intersectionPropertyReducesToNever(t *Type, name string) bool { + if prop := t.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment.Get(name); prop != nil { + return c.isNeverReducedProperty(prop) + } + var sources unionOrIntersectionPropertySources + if !c.getUnionOrIntersectionPropertySources(t, name, true /*skipObjectFunctionPropertyAugment*/, &sources) { + return false + } + defer func() { + c.releaseTemporarySymbolList(sources.props) + if sources.indexTypes != nil { + c.releaseTemporaryTypeArguments(sources.indexTypes) + } + }() + if !sources.isSynthetic() { + if !sources.mergedInstantiations { + return c.isNeverReducedProperty(sources.singleProp) + } + prop := c.getUnionOrIntersectionProperty(t, name, true /*skipObjectFunctionPropertyAugment*/) + return prop != nil && c.isNeverReducedProperty(prop) + } + props := sources.props + var singlePropBuf [1]*ast.Symbol + if len(props) == 0 { + singlePropBuf[0] = sources.singleProp + props = singlePropBuf[:] + } + checkFlags := sources.checkFlags + var firstType *Type + var firstValueDeclaration *ast.Node + hasNonUniformValueDeclaration := false + propTypes := c.getTemporaryTypeArguments(0)[:0] + defer c.releaseTemporaryTypeArguments(propTypes) + for _, prop := range props { + if firstValueDeclaration == nil { + firstValueDeclaration = prop.ValueDeclaration() + } else if prop.ValueDeclaration() != nil && prop.ValueDeclaration() != firstValueDeclaration { + hasNonUniformValueDeclaration = true + } + propType := c.getTypeOfSymbol(prop) + if firstType == nil { + firstType = propType + } + if propType != firstType { + checkFlags |= ast.CheckFlagsHasNonUniformType + } + if isLiteralType(propType) || c.isPatternLiteralType(propType) { + checkFlags |= ast.CheckFlagsHasLiteralType + } + if propType.flags&TypeFlagsNever != 0 && propType != c.uniqueLiteralType { + checkFlags |= ast.CheckFlagsHasNeverType + } + propTypes = append(propTypes, propType) + } + propTypes = append(propTypes, sources.indexTypes...) + hasValueDeclaration := !hasNonUniformValueDeclaration && firstValueDeclaration != nil + if !hasValueDeclaration && checkFlags&ast.CheckFlagsContainsPrivate != 0 { + return true + } + return (sources.propFlags|sources.optionalFlag)&ast.SymbolFlagsOptional == 0 && + checkFlags&(ast.CheckFlagsNonUniformAndLiteral|ast.CheckFlagsHasNeverType) == ast.CheckFlagsNonUniformAndLiteral && + c.getIntersectionType(propTypes).flags&TypeFlagsNever != 0 +} + func (c *Checker) getReducedUnionType(unionType *Type) *Type { reducedTypes := core.SameMap(unionType.Types(), c.getReducedType) if core.Same(reducedTypes, unionType.Types()) { @@ -22481,10 +24361,9 @@ func (c *Checker) isNeverReducedProperty(prop *ast.Symbol) bool { } func (c *Checker) getReducedApparentType(t *Type) *Type { - // Since getApparentType may return a non-reduced union or intersection type, we need to perform - // type reduction both before and after obtaining the apparent type. For example, given a type parameter - // 'T extends A | B', the type 'T & X' becomes 'A & X | B & X' after obtaining the apparent type, and - // that type may need further reduction to remove empty intersections. + if t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped == 0 { + return t + } return c.getReducedType(c.getApparentType(c.getReducedType(t))) } @@ -22670,25 +24549,29 @@ func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { } func (c *Checker) getNamedMembers(members ast.SymbolTable, container *ast.Symbol) []*ast.Symbol { - if len(members) == 0 { + if members.Len() == 0 { return nil } // For classes and interfaces, we store explicitly declared members ahead of inherited members. This ensures we process // explicitly declared members first in type relations, which is beneficial because explicitly declared members are more // likely to contain discriminating differences. See for example https://github.com/microsoft/TypeScript/tsc/issues/1968. - result := make([]*ast.Symbol, 0, len(members)) - var containedCount int - if container != nil && container.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 { - for id, symbol := range members { - if c.isNamedMember(symbol, id) && c.isDeclarationContainedBy(symbol, container) { - result = append(result, symbol) - } + result := make([]*ast.Symbol, 0, members.Len()) + for id, symbol := range members.All() { + if c.isNamedMember(symbol, id) { + result = append(result, symbol) } - containedCount = len(result) } - for id, symbol := range members { - if c.isNamedMember(symbol, id) && (container == nil || container.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) == 0 || !c.isDeclarationContainedBy(symbol, container)) { - result = append(result, symbol) + if container == nil || container.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) == 0 { + c.sortSymbols(result) + return result + } + // Move the members declared in the container to the front. Both parts are sorted below, so the order the split + // leaves them in does not matter. + containedCount := 0 + for i, symbol := range result { + if c.isDeclarationContainedBy(symbol, container) { + result[containedCount], result[i] = result[i], result[containedCount] + containedCount++ } } c.sortSymbols(result[:containedCount]) @@ -22721,18 +24604,50 @@ func (c *Checker) symbolIsValueEx(symbol *ast.Symbol, includeTypeOnlyMembers boo } func (c *Checker) instantiateType(t *Type, m *TypeMapper) *Type { - return c.instantiateTypeWithAlias(t, m, nil /*alias*/) + if t == nil || m == nil || (t.flags&(TypeFlagsInstantiable|TypeFlagsObject|TypeFlagsUnionOrIntersection) == 0 || t.objectFlags&(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables) == ObjectFlagsCouldContainTypeVariablesComputed) && (t.alias == nil || len(t.alias.typeArguments) == 0) { + return t + } + return c.instantiateTypeWithAliasSlow(t, m, nil /*alias*/) +} + +func (c *Checker) anyCouldContainTypeVariables(types []*Type) bool { + return slices.ContainsFunc(types, c.couldContainTypeVariables) } func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAlias) *Type { + if t == nil || m == nil || (t.flags&(TypeFlagsInstantiable|TypeFlagsObject|TypeFlagsUnionOrIntersection) == 0 || t.objectFlags&(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables) == ObjectFlagsCouldContainTypeVariablesComputed) && (t.alias == nil || len(t.alias.typeArguments) == 0) { + return t + } + return c.instantiateTypeWithAliasSlow(t, m, alias) +} + +func getInstantiationCacheKeyWithAlias(t *Type, alias *TypeAlias) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeType(t) + b.writeAlias(alias) + return b.releaseHash() + } + var b keyBuilder + b.writeType(t) + b.writeAlias(alias) + return b.hash() +} + +func (c *Checker) instantiateTypeWithAliasSlow(t *Type, m *TypeMapper, alias *TypeAlias) *Type { // Check for type variables in the alias, so things like `type Brand = number & {}` can potentially be copied with new alias type args, despite them being unreferenced. // This is the behavior most people using aliases expect, and prevents the cache from leaking type parameters outside their scope of validity. // tests/cases/compiler/declarationEmitArrowFunctionNoRenaming.ts contains an example of this, which previously only worked in strada via some input node reuse logic instead. - if t == nil || m == nil || !(c.couldContainTypeVariables(t) || (t.alias != nil && len(t.alias.typeArguments) > 0 && core.Some(t.alias.typeArguments, c.couldContainTypeVariables))) { + if !(c.couldContainTypeVariables(t) || (t.alias != nil && len(t.alias.typeArguments) > 0 && c.anyCouldContainTypeVariables(t.alias.typeArguments))) { return t } - if len(c.instantiationStack) == 100 || c.instantiationCount >= 5_000_000 { - // We have reached 100 recursive type instantiations, or 5M type instantiations caused by the same statement + maxInstDepth := 100 + maxInstCount := uint32(5_000_000) + if runtime.GOARCH == "wasm" { + maxInstDepth = 500 + maxInstCount = 50_000_000 + } + if len(c.instantiationStack) == maxInstDepth || c.instantiationCount >= maxInstCount { // or expression. There is a very high likelihood we're dealing with a combination of infinite generic types // that perpetually generate new type identities, so we stop the recursion here by yielding the error type. if tr := c.tracer; tr != nil { @@ -22750,16 +24665,19 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl return c.errorType } index := c.findActiveMapper(m) + var key CacheHashKey if index == -1 { c.pushActiveMapper(m) - } - var b keyBuilder - b.writeType(t) - b.writeAlias(alias) - key := b.hash() - cache := c.activeTypeMappersCaches[core.IfElse(index != -1, index, len(c.activeTypeMappersCaches)-1)] - if cachedType, ok := cache[key]; ok { - return cachedType + } else { + if alias == nil { + h := uint64(t.id) * 0x9e3779b97f4a7c15 + key = CacheHashKey{Lo: h ^ (h >> 32), Hi: uint64(t.id)} + } else { + key = getInstantiationCacheKeyWithAlias(t, alias) + } + if cachedType := c.activeTypeMappersCaches[index].get(key); cachedType != nil { + return cachedType + } } c.TotalInstantiationCount++ c.instantiationCount++ @@ -22768,7 +24686,7 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl if index == -1 { c.popActiveMapper() } else { - cache[key] = result + c.activeTypeMappersCaches[index].set(key, result) } c.instantiationStack[len(c.instantiationStack)-1] = nil c.instantiationStack = c.instantiationStack[:len(c.instantiationStack)-1] @@ -22797,55 +24715,54 @@ func (c *Checker) getCircularTypeNames() []string { func (c *Checker) pushActiveMapper(mapper *TypeMapper) { c.activeMappers = append(c.activeMappers, mapper) - lastIndex := len(c.activeTypeMappersCaches) if cap(c.activeTypeMappersCaches) > lastIndex { - // The cap may contain an empty map from popActiveMapper; reuse it. c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1] - if c.activeTypeMappersCaches[lastIndex] == nil { - c.activeTypeMappersCaches[lastIndex] = make(map[CacheHashKey]*Type, 1) - } } else { - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[CacheHashKey]*Type, 1)) + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, activeMapperCache{}) } } func (c *Checker) popActiveMapper() { c.activeMappers[len(c.activeMappers)-1] = nil c.activeMappers = c.activeMappers[:len(c.activeMappers)-1] - - // Clear the map, but leave it in the list for later reuse. lastIndex := len(c.activeTypeMappersCaches) - 1 - clear(c.activeTypeMappersCaches[lastIndex]) + c.activeTypeMappersCaches[lastIndex].clear() c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex] } func (c *Checker) findActiveMapper(mapper *TypeMapper) int { - return core.FindLastIndex(c.activeMappers, func(m *TypeMapper) bool { return m == mapper }) + for i := len(c.activeMappers) - 1; i >= 0; i-- { //nolint:modernize + if c.activeMappers[i] == mapper { + return i + } + } + return -1 } func (c *Checker) clearActiveMapperCaches() { - for _, cache := range c.activeTypeMappersCaches { - clear(cache) + for i := range c.activeTypeMappersCaches { + c.activeTypeMappersCaches[i].clear() } } // Return true if the given type could possibly reference a type parameter for which // we perform type inference (i.e. a type parameter of a generic function). We cache // results for union and intersection types for performance reasons. -func (c *Checker) couldContainTypeVariablesWorker(t *Type) bool { - if t.flags&TypeFlagsStructuredOrInstantiable == 0 { - return false +func (c *Checker) couldContainTypeVariables(t *Type) bool { + if t.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed != 0 { + return t.objectFlags&ObjectFlagsCouldContainTypeVariables != 0 } + return c.couldContainTypeVariablesSlow(t) +} + +func (c *Checker) couldContainTypeVariablesSlow(t *Type) bool { objectFlags := t.objectFlags - if objectFlags&ObjectFlagsCouldContainTypeVariablesComputed != 0 { - return objectFlags&ObjectFlagsCouldContainTypeVariables != 0 - } result := t.flags&TypeFlagsInstantiable != 0 || - t.flags&TypeFlagsObject != 0 && !c.isNonGenericTopLevelType(t) && (objectFlags&ObjectFlagsReference != 0 && (t.AsTypeReference().node != nil || core.Some(c.getTypeArguments(t), c.couldContainTypeVariables)) || + t.flags&TypeFlagsObject != 0 && !c.isNonGenericTopLevelType(t) && (objectFlags&ObjectFlagsReference != 0 && (t.AsTypeReference().node != nil || c.anyCouldContainTypeVariables(c.getTypeArguments(t))) || objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) != 0 && t.symbol.Declarations() != nil || objectFlags&(ObjectFlagsMapped|ObjectFlagsReverseMapped|ObjectFlagsObjectRestType|ObjectFlagsInstantiationExpressionType) != 0) || - t.flags&TypeFlagsUnionOrIntersection != 0 && t.flags&TypeFlagsEnumLiteral == 0 && !c.isNonGenericTopLevelType(t) && core.Some(t.Types(), c.couldContainTypeVariables) + t.flags&TypeFlagsUnionOrIntersection != 0 && t.flags&TypeFlagsEnumLiteral == 0 && !c.isNonGenericTopLevelType(t) && c.anyCouldContainTypeVariables(t.Types()) t.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(result, ObjectFlagsCouldContainTypeVariables, 0) return result } @@ -22879,11 +24796,28 @@ func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias if objectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { if objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil { resolvedTypeArguments := t.AsTypeReference().resolvedTypeArguments - newTypeArguments := c.instantiateTypes(resolvedTypeArguments, m) - if core.Same(newTypeArguments, resolvedTypeArguments) { + newTypeArguments, changed := c.instantiateTypesTemp(resolvedTypeArguments, m) + if !changed { return t } - return c.createNormalizedTypeReference(t.Target(), newTypeArguments) + target := t.Target() + if target.objectFlags&ObjectFlagsTuple == 0 || target.AsTupleType().combinedFlags&ElementFlagsNonRequired == 0 { + if slices.Equal(newTypeArguments, target.AsInterfaceType().resolvedTypeArguments) { + c.releaseTemporaryTypeArguments(newTypeArguments) + return target + } + id := getTypeListKey(newTypeArguments) + if inst := target.AsInterfaceType().instantiations.get(id); inst != nil { + c.releaseTemporaryTypeArguments(newTypeArguments) + return inst + } + cloned := c.typeListArena.Clone(newTypeArguments) + c.releaseTemporaryTypeArguments(newTypeArguments) + return c.createTypeReference(target, cloned) + } + cloned := c.typeListArena.Clone(newTypeArguments) + c.releaseTemporaryTypeArguments(newTypeArguments) + return c.createNormalizedTypeReference(target, cloned) } if objectFlags&ObjectFlagsReverseMapped != 0 { return c.instantiateReverseMappedType(t, m) @@ -22900,17 +24834,23 @@ func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias } } types := source.Types() - newTypes := c.instantiateTypes(types, m) - if core.Same(newTypes, types) && alias.Symbol() == t.alias.Symbol() { + newTypes, changed := c.instantiateTypesTemp(types, m) + if !changed && alias.Symbol() == t.alias.Symbol() { return t } if alias == nil { alias = c.instantiateTypeAlias(t.alias, m) } + var res *Type if source.flags&TypeFlagsIntersection != 0 { - return c.getIntersectionTypeEx(newTypes, IntersectionFlagsNone, alias) + res = c.getIntersectionTypeEx(newTypes, IntersectionFlagsNone, alias) + } else { + res = c.getUnionTypeEx(newTypes, UnionReductionLiteral, alias, nil /*origin*/) + } + if changed { + c.releaseTemporaryTypeArguments(newTypes) } - return c.getUnionTypeEx(newTypes, UnionReductionLiteral, alias, nil /*origin*/) + return res case flags&TypeFlagsIndex != 0: return c.getIndexType(c.instantiateType(t.Target(), m)) case flags&TypeFlagsIndexedAccess != 0: @@ -22918,13 +24858,37 @@ func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias alias = c.instantiateTypeAlias(t.alias, m) } d := t.AsIndexedAccessType() - return c.getIndexedAccessTypeEx(c.instantiateType(d.objectType, m), c.instantiateType(d.indexType, m), d.accessFlags, nil /*accessNode*/, alias) + objectType := c.instantiateType(d.objectType, m) + if d.accessFlags == AccessFlagsNone && isTupleType(objectType) && !c.compilerOptions.NoUncheckedIndexedAccess.IsTrue() { + tuple := objectType.TargetTupleType() + if tuple.combinedFlags&(ElementFlagsOptional|ElementFlagsVariable) == 0 && d.indexType.flags&TypeFlagsIntersection != 0 { + if iTypes := d.indexType.Types(); len(iTypes) == 2 { + var keyCand *Type + if iTypes[0].flags&TypeFlagsIndex != 0 && iTypes[0].AsIndexType().target == d.objectType && iTypes[0].AsIndexType().indexFlags == IndexFlagsNone { + keyCand = c.instantiateType(iTypes[1], m) + } else if iTypes[1].flags&TypeFlagsIndex != 0 && iTypes[1].AsIndexType().target == d.objectType && iTypes[1].AsIndexType().indexFlags == IndexFlagsNone { + keyCand = c.instantiateType(iTypes[0], m) + } + if keyCand != nil { + if fast := c.tryFastTupleIndexedAccess(objectType, keyCand); fast != nil { + return fast + } + } + } + } + } + return c.getIndexedAccessTypeEx(objectType, c.instantiateType(d.indexType, m), d.accessFlags, nil /*accessNode*/, alias) case flags&TypeFlagsTemplateLiteral != 0: - return c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, c.instantiateTypes(t.AsTemplateLiteralType().types, m)) + newTypes, changed := c.instantiateTypesTemp(t.AsTemplateLiteralType().types, m) + res := c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, newTypes) + if changed { + c.releaseTemporaryTypeArguments(newTypes) + } + return res case flags&TypeFlagsStringMapping != 0: return c.getStringMappingType(t.symbol, c.instantiateType(t.AsStringMappingType().target, m)) case flags&TypeFlagsConditional != 0: - return c.getConditionalTypeInstantiation(t, c.combineTypeMappers(t.AsConditionalType().mapper, m), false /*forConstraint*/, alias) + return c.getConditionalTypeInstantiationEx(t, t.AsConditionalType().mapper, m, false /*forConstraint*/, alias) case flags&TypeFlagsSubstitution != 0: newBaseType := c.instantiateType(t.AsSubstitutionType().baseType, m) if c.isNoInferType(t) { @@ -22983,15 +24947,17 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type typeParameters = c.getOuterTypeParameters(declaration, true /*includeThisTypes*/) if len(target.alias.TypeArguments()) == 0 { if t.objectFlags&(ObjectFlagsReference|ObjectFlagsInstantiationExpressionType) != 0 { - typeParameters = core.Filter(typeParameters, func(tp *Type) bool { - return c.isTypeParameterPossiblyReferenced(tp, declaration) - }) + typeParameters = c.filterPossiblyReferencedTypeParameters(typeParameters, declaration) } else if target.symbol.Flags()&(ast.SymbolFlagsMethod|ast.SymbolFlagsTypeLiteral) != 0 { - typeParameters = core.Filter(typeParameters, func(tp *Type) bool { - return core.Some(t.symbol.Declarations(), func(d *ast.Node) bool { - return c.isTypeParameterPossiblyReferenced(tp, d) + if decls := t.symbol.Declarations(); len(decls) == 1 { + typeParameters = c.filterPossiblyReferencedTypeParameters(typeParameters, decls[0]) + } else { + typeParameters = core.Filter(typeParameters, func(tp *Type) bool { + return core.Some(decls, func(d *ast.Node) bool { + return c.isTypeParameterPossiblyReferenced(tp, d) + }) }) - }) + } } } if typeParameters == nil { @@ -23005,23 +24971,48 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the // mapper to the type parameters to produce the effective list of type arguments, and compute the // instantiation cache key from the type IDs of the type arguments. - typeArguments := make([]*Type, len(typeParameters)) + m1 := t.Mapper() + typeArguments := c.getTemporaryTypeArguments(len(typeParameters)) + changed := false for i, tp := range typeParameters { - typeArguments[i] = c.mapTypeWithCompositeMapper(tp, t.Mapper(), m) + if m1 != nil { + if t1 := m1.Map(tp); t1 != tp { + t2 := c.instantiateType(t1, m) + if t2 != t1 { + changed = true + } + typeArguments[i] = t2 + continue + } + } + t2 := m.Map(tp) + if t2 != tp { + changed = true + } + typeArguments[i] = t2 } newAlias := alias if newAlias == nil { newAlias = c.instantiateTypeAlias(t.alias, m) } + if !changed && newAlias == t.alias && t.objectFlags&ObjectFlagsSingleSignatureType == 0 && (t != target || t.objectFlags&ObjectFlagsMapped == 0) { + c.releaseTemporaryTypeArguments(typeArguments) + return t + } + if newAlias == target.alias && t.objectFlags&ObjectFlagsSingleSignatureType == 0 && slices.Equal(typeArguments, typeParameters) { + c.releaseTemporaryTypeArguments(typeArguments) + return target + } data := target.AsObjectType() key := getTypeInstantiationKey(typeArguments, newAlias, t.objectFlags&ObjectFlagsSingleSignatureType != 0) - if data.instantiations == nil { - data.instantiations = make(map[CacheHashKey]*Type) - data.instantiations[getTypeInstantiationKey(typeParameters, target.alias, false)] = target - } - result := data.instantiations[key] + result := data.instantiations.get(key) if result == nil { - newMapper := newTypeMapper(typeParameters, typeArguments) + var newMapper *TypeMapper + if len(typeParameters) == 1 { + newMapper = newSimpleTypeMapper(typeParameters[0], typeArguments[0]) + } else { + newMapper = newArrayTypeMapper(typeParameters, c.typeListArena.Clone(typeArguments)) + } if target.objectFlags&ObjectFlagsSingleSignatureType != 0 && m != nil { newMapper = c.combineTypeMappers(newMapper, m) } @@ -23033,10 +25024,10 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type default: result = c.instantiateAnonymousType(target, newMapper, newAlias) } - data.instantiations[key] = result + data.setInstantiation(c, key, result) if result.flags&TypeFlagsObjectFlagsType != 0 && result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { // if `result` is one of the object types we tried to make (it may not be, due to how `instantiateMappedType` works), we can carry forward the type variable containment check from the input type arguments - resultCouldContainObjectFlags := core.Some(typeArguments, c.couldContainTypeVariables) + resultCouldContainObjectFlags := c.anyCouldContainTypeVariables(typeArguments) if result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { if result.objectFlags&(ObjectFlagsMapped|ObjectFlagsAnonymous|ObjectFlagsReference) != 0 { result.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariables, 0) @@ -23049,10 +25040,11 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type } } } + c.releaseTemporaryTypeArguments(typeArguments) return result } -func (c *Checker) isTypeParameterPossiblyReferenced(tp *Type, node *ast.Node) bool { +func (c *Checker) typeParameterReferenceInSubtree(tp *Type, node *ast.Node) bool { var containsReference func(*ast.Node) bool containsReference = func(node *ast.Node) bool { switch node.Kind { @@ -23091,18 +25083,67 @@ func (c *Checker) isTypeParameterPossiblyReferenced(tp *Type, node *ast.Node) bo } return node.ForEachChild(containsReference) } + return containsReference(node) +} + +func (c *Checker) filterPossiblyReferencedTypeParameters(candidates []*Type, node *ast.Node) []*Type { + if len(candidates) <= 1 { + return core.Filter(candidates, func(tp *Type) bool { return c.isTypeParameterPossiblyReferenced(tp, node) }) + } + referenced := make(map[*ast.Symbol]bool) + thisReferenced := false + uncertain := false + var collect func(*ast.Node) bool + collect = func(n *ast.Node) bool { + switch n.Kind { + case ast.KindThisType: + thisReferenced = true + case ast.KindTypeReference: + if len(n.TypeArguments()) == 0 { + if sym := c.getSymbolFromTypeReference(n); sym != nil { + referenced[sym] = true + } + } + case ast.KindTypeQuery, ast.KindMethodDeclaration, ast.KindMethodSignature: + uncertain = true + return true + } + return n.ForEachChild(collect) + } + collect(node) + if uncertain { + return core.Filter(candidates, func(tp *Type) bool { return c.isTypeParameterPossiblyReferenced(tp, node) }) + } + return core.Filter(candidates, func(tp *Type) bool { + if tp.symbol == nil || len(tp.symbol.Declarations()) != 1 { + return true + } + container := tp.symbol.Declarations()[0].Parent + for n := node; n != container; n = n.Parent { + if n == nil || ast.IsBlock(n) || ast.IsConditionalTypeNode(n) && c.typeParameterReferenceInSubtree(tp, n.AsConditionalTypeNode().ExtendsType) { + return true + } + } + if tp.AsTypeParameter().isThisType { + return thisReferenced + } + return referenced[tp.symbol] + }) +} + +func (c *Checker) isTypeParameterPossiblyReferenced(tp *Type, node *ast.Node) bool { // If the type parameter doesn't have exactly one declaration, if there are intervening statement blocks // between the node and the type parameter declaration, if the node contains actual references to the - // type parameter, or if the node contains type queries that we can't prove couldn't contain references to the type parameter, - // we consider the type parameter possibly referenced. + // type parameter, or if the node contains type queries that we can't prove couldn't contain references to the + // type parameter, we consider the type parameter possibly referenced. if tp.symbol != nil && len(tp.symbol.Declarations()) == 1 { container := tp.symbol.Declarations()[0].Parent for n := node; n != container; n = n.Parent { - if n == nil || ast.IsBlock(n) || ast.IsConditionalTypeNode(n) && containsReference(n.AsConditionalTypeNode().ExtendsType) { + if n == nil || ast.IsBlock(n) || ast.IsConditionalTypeNode(n) && c.typeParameterReferenceInSubtree(tp, n.AsConditionalTypeNode().ExtendsType) { return true } } - return containsReference(node) + return c.typeParameterReferenceInSubtree(tp, node) } return true } @@ -23118,6 +25159,7 @@ func (c *Checker) instantiateAnonymousType(t *Type, m *TypeMapper, alias *TypeAl result.AsMappedType().typeParameter = freshTypeParameter m = c.combineTypeMappers(newSimpleTypeMapper(origTypeParameter, freshTypeParameter), m) freshTypeParameter.AsTypeParameter().mapper = m + markMapperEscaped(m) case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0: result.AsInstantiationExpressionType().node = t.AsInstantiationExpressionType().node } @@ -23131,36 +25173,103 @@ func (c *Checker) instantiateAnonymousType(t *Type, m *TypeMapper, alias *TypeAl d := result.AsObjectType() d.target = t d.mapper = m + markMapperEscaped(m) return result } func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { + return c.getConditionalTypeInstantiationEx(t, nil, mapper, forConstraint, alias) +} + +func (c *Checker) getConditionalTypeInstantiationEx(t *Type, m1 *TypeMapper, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { root := t.AsConditionalType().root if len(root.outerTypeParameters) != 0 { // We are instantiating a conditional type that has one or more type parameters in scope. Apply the // mapper to the type parameters to produce the effective list of type arguments, and compute the // instantiation cache key from the type IDs of the type arguments. - typeArguments := core.Map(root.outerTypeParameters, mapper.Map) + outerTypeParameters := root.outerTypeParameters + typeArguments := c.getTemporaryTypeArguments(len(outerTypeParameters)) + changed := false + for i, tp := range outerTypeParameters { + // What CompositeTypeMapper.Map does. + if m1 != nil { + if t1 := m1.Map(tp); t1 != tp { + t2 := c.instantiateType(t1, mapper) + if t2 != t1 { + changed = true + } + typeArguments[i] = t2 + continue + } + } + t2 := mapper.Map(tp) + if t2 != tp { + changed = true + } + typeArguments[i] = t2 + } + if !changed && !forConstraint && (alias == nil && t.alias == nil || alias == t.alias) && m1 == t.AsConditionalType().mapper { + c.releaseTemporaryTypeArguments(typeArguments) + return t + } key := getConditionalTypeKey(typeArguments, alias, forConstraint) - result := root.instantiations[key] + result := root.instantiations.get(key) if result == nil { - newMapper := newTypeMapper(root.outerTypeParameters, typeArguments) checkType := root.checkType - var distributionType *Type - if root.isDistributive { - distributionType = c.getReducedType(getMappedType(checkType, newMapper)) - } - // Distributive conditional types are distributed over union types. For example, when the - // distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the - // result is (A extends U ? X : Y) | (B extends U ? X : Y). - if distributionType != nil && checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 { - result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { - return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil) - }, alias) + if len(outerTypeParameters) == 1 && root.isDistributive && getNonDistributedTypeParameter(checkType) == outerTypeParameters[0] { + tp := outerTypeParameters[0] + arg := typeArguments[0] + c.releaseTemporaryTypeArguments(typeArguments) + distributionType := c.getReducedType(arg) + if checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 { + var newMapper *TypeMapper + result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { + if t == tp { + if newMapper == nil { + newMapper = newSimpleTypeMapper(tp, arg) + } + return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil) + } + if checkType == tp { + elemKey := packedSingleTypeKey(packedConditionalKeyTag, t, forConstraint) + res := root.instantiations.get(elemKey) + if res == nil { + res = c.getConditionalType(root, newSimpleTypeMapper(tp, t), forConstraint, nil) + root.instantiations.setSmall(c, elemKey, res) + } + return res + } + return c.getConditionalType(root, newSimpleTypeMapper(tp, t), forConstraint, nil) + }, alias) + } else { + result = c.getConditionalType(root, newSimpleTypeMapper(tp, arg), forConstraint, alias) + } } else { - result = c.getConditionalType(root, newMapper, forConstraint, alias) + var newMapper *TypeMapper + if len(outerTypeParameters) == 1 { + newMapper = newSimpleTypeMapper(outerTypeParameters[0], typeArguments[0]) + } else { + newMapper = newArrayTypeMapper(outerTypeParameters, c.typeListArena.Clone(typeArguments)) + } + c.releaseTemporaryTypeArguments(typeArguments) + var distributionType *Type + if root.isDistributive { + distributionType = c.getReducedType(getMappedType(checkType, newMapper)) + } + // Distributive conditional types are distributed over union types. For example, when the + // distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the + // result is (A extends U ? X : Y) | (B extends U ? X : Y). + if distributionType != nil && checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 { + result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { + return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil) + }, alias) + } else { + result = c.getConditionalType(root, newMapper, forConstraint, alias) + } } - root.instantiations[key] = result + root.instantiations.setSmall(c, key, result) + } else { + c.releaseTemporaryTypeArguments(typeArguments) } return result } @@ -23198,27 +25307,39 @@ func (c *Checker) instantiateMappedType(t *Type, m *TypeMapper, alias *TypeAlias // { [P in keyof A]: X } | undefined. d := t.AsMappedType() typeVariable := c.getHomomorphicTypeVariable(t) - var instantiateConstituent func(*Type) *Type - instantiateConstituent = func(s *Type) *Type { - if s.flags&(TypeFlagsAnyOrUnknown|TypeFlagsInstantiableNonPrimitive|TypeFlagsObject|TypeFlagsIntersection) == 0 || s == c.wildcardType || c.isErrorType(s) { - return s - } - if d.declaration.NameType == nil { - if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && c.findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) < 0 && c.hasArrayOrTypeTypeConstraint(typeVariable) { - return c.instantiateMappedArrayType(s, t, prependTypeMapping(typeVariable, s, m)) - } - if isTupleType(s) { - return c.instantiateMappedTupleType(s, t, typeVariable, m) - } - if c.isArrayOrTupleOrIntersection(s) { - return c.getIntersectionType(core.Map(s.Types(), instantiateConstituent)) - } - } - return c.instantiateAnonymousType(t, prependTypeMapping(typeVariable, s, m), nil) - } if typeVariable != nil { mappedTypeVariable := c.instantiateType(typeVariable, m) if typeVariable != mappedTypeVariable { + var instantiateConstituent func(*Type) *Type + instantiateConstituent = func(s *Type) *Type { + if s.flags&(TypeFlagsAnyOrUnknown|TypeFlagsInstantiableNonPrimitive|TypeFlagsObject|TypeFlagsIntersection) == 0 || s == c.wildcardType || c.isErrorType(s) { + return s + } + if d.declaration.NameType == nil { + if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && c.findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) < 0 && c.hasArrayOrTypeTypeConstraint(typeVariable) { + return c.instantiateMappedArrayType(s, t, prependTypeMapping(typeVariable, s, m)) + } + if isTupleType(s) { + return c.instantiateMappedTupleType(s, t, typeVariable, m) + } + if c.isArrayOrTupleOrIntersection(s) { + return c.getIntersectionType(core.Map(s.Types(), instantiateConstituent)) + } + } + if m.kind == TypeMapperKindSimple && s != mappedTypeVariable && s != typeVariable && !typeVariable.AsTypeParameter().isDistributed && t.objectFlags&ObjectFlagsSingleSignatureType == 0 { + elemKey := packedSingleTypeKey(packedInstantiationKeyTag, s, false) + res := t.AsObjectType().instantiations.get(elemKey) + if res == nil { + res = c.instantiateAnonymousType(t, prependTypeMapping(typeVariable, s, m), nil) + if res.flags&TypeFlagsObjectFlagsType != 0 && res.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 && res.objectFlags&(ObjectFlagsMapped|ObjectFlagsAnonymous|ObjectFlagsReference) != 0 { + res.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(c.couldContainTypeVariables(s), ObjectFlagsCouldContainTypeVariables, 0) + } + t.AsObjectType().setInstantiation(c, elemKey, res) + } + return res + } + return c.instantiateAnonymousType(t, prependTypeMapping(typeVariable, s, m), nil) + } return c.mapTypeWithAlias(c.getReducedType(mappedTypeVariable), instantiateConstituent, alias) } } @@ -23260,8 +25381,17 @@ func (c *Checker) instantiateMappedTupleType(tupleType *Type, mappedType *Type, } modifiers := getMappedTypeModifiers(mappedType) elementTypes := c.getElementTypes(tupleType) + tupleTarget := tupleType.TargetTupleType() + if modifiers == 0 && fixedLength == len(elementTypes) && tupleTarget.combinedFlags&(ElementFlagsOptional|ElementFlagsVariable) == 0 { + if fast := c.tryFastMappedTupleSet(tupleType, mappedType, typeVariable, elementTypes, elementInfos, tupleTarget.readonly, fixedMapper); fast != nil { + return fast + } + } newElementTypes := make([]*Type, len(elementTypes)) - newElementInfos := slices.Clone(elementInfos) + newElementInfos := elementInfos + if modifiers != 0 { + newElementInfos = slices.Clone(elementInfos) + } for i, e := range elementTypes { flags := elementInfos[i].flags var mapped *Type @@ -23272,9 +25402,9 @@ func (c *Checker) instantiateMappedTupleType(tupleType *Type, mappedType *Type, mapped = c.instantiateType(mappedType, prependTypeMapping(typeVariable, e, m)) default: mapped = c.getElementTypeOfArrayType(c.instantiateType(mappedType, prependTypeMapping(typeVariable, c.createArrayType(e), m))) - if mapped == nil { - mapped = c.unknownType - } + } + if mapped == nil { + mapped = c.unknownType } switch { case modifiers&MappedTypeModifiersIncludeOptional != 0: @@ -23288,11 +25418,102 @@ func (c *Checker) instantiateMappedTupleType(tupleType *Type, mappedType *Type, } newElementTypes[i] = mapped } - newReadonly := getModifiedReadonlyState(tupleType.TargetTupleType().readonly, getMappedTypeModifiers(mappedType)) + newReadonly := getModifiedReadonlyState(tupleTarget.readonly, modifiers) if slices.Contains(newElementTypes, c.errorType) { return c.errorType } - return c.createTupleTypeEx(newElementTypes, newElementInfos, newReadonly) + return c.createTupleTypeEx(newElementTypes, newElementInfos, newReadonly) +} + +func (c *Checker) tryFastMappedTupleSet(tupleType *Type, mappedType *Type, typeVariable *Type, elementTypes []*Type, elementInfos []TupleElementInfo, readonly bool, fixedMapper *TypeMapper) *Type { + target := core.OrElse(mappedType.AsMappedType().target, mappedType) + if target.AsMappedType().declaration.NameType != nil { + return nil + } + template := c.getTemplateTypeFromMappedType(target) + if template.flags&TypeFlagsConditional == 0 { + return nil + } + cond := template.AsConditionalType() + if cond.mapper != nil { + return nil + } + root := cond.root + if root.isDistributive && len(root.inferTypeParameters) != 0 { + return nil + } + typeParam := c.getTypeParameterFromMappedType(mappedType) + if root.checkType != typeParam { + return nil + } + falseNode := ast.SkipTypeParentheses(root.node.FalseType) + if falseNode.Kind != ast.KindIndexedAccessType { + return nil + } + ia := falseNode.AsIndexedAccessTypeNode() + if c.getTypeFromTypeNode(ia.ObjectType) != typeVariable || c.getTypeFromTypeNode(ia.IndexType) != typeParam { + return nil + } + instExtends := c.instantiateType(root.extendsType, fixedMapper) + if instExtends.flags&TypeFlagsStringLiteral == 0 { + return nil + } + idx, ok := parseSmallTupleIndex(instExtends.AsLiteralType().value.(string)) + if !ok || idx < 0 || idx >= len(elementTypes) { + return nil + } + trueMapper := appendTypeMapping(fixedMapper, typeParam, instExtends) + mapped := c.instantiateType(c.getTrueTypeFromConditionalType(template), trueMapper) + if mapped == nil { + mapped = c.unknownType + } + if mapped == c.errorType { + return c.errorType + } + newElementTypes := slices.Clone(elementTypes) + newElementTypes[idx] = mapped + return c.createTupleTypeEx(newElementTypes, elementInfos, readonly) +} + +func parseSmallTupleIndex(s string) (int, bool) { + if len(s) == 0 || len(s) > 6 { + return 0, false + } + if len(s) > 1 && s[0] == '0' { + return 0, false + } + n := 0 + for i := range len(s) { + ch := s[i] + if ch < '0' || ch > '9' { + return 0, false + } + n = n*10 + int(ch-'0') + } + return n, true +} + +func (c *Checker) tryFastTupleIndexedAccess(objectType *Type, indexType *Type) *Type { + if indexType.flags&TypeFlagsNumberLiteral != 0 { + f := float64(indexType.AsLiteralType().value.(jsnum.Number)) + idx := int(f) + if float64(idx) == f && idx >= 0 { + if typeArgs := c.getTypeArguments(objectType); idx < len(typeArgs) { + return typeArgs[idx] + } + } + } else if indexType.flags&TypeFlagsStringLiteral != 0 { + s := indexType.AsLiteralType().value.(string) + if s == "length" { + return c.getNumberLiteralType(jsnum.Number(len(c.getTypeArguments(objectType)))) + } + if idx, ok := parseSmallTupleIndex(s); ok { + if typeArgs := c.getTypeArguments(objectType); idx < len(typeArgs) { + return typeArgs[idx] + } + } + } + return nil } func (c *Checker) instantiateMappedTypeTemplate(t *Type, key *Type, isOptional bool, m *TypeMapper) *Type { @@ -23377,8 +25598,9 @@ func (c *Checker) getApparentMappedTypeKeys(nameType *Type, targetType *Type) *T } func (c *Checker) forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(t *Type, include TypeFlags, stringsOnly bool, cb func(keyType *Type)) { - for _, prop := range c.getPropertiesOfType(t) { - cb(c.getLiteralTypeFromProperty(prop, include, false)) + props, _ := c.getPropertiesOfTypeLazily(t) + for _, prop := range props { + cb(c.getLiteralTypeFromProperty(prop, include, stringsOnly)) } if t.flags&TypeFlagsAny != 0 { cb(c.stringType) @@ -23409,43 +25631,138 @@ func (c *Checker) instantiateReverseMappedType(t *Type, m *TypeMapper) *Type { // Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable } -func (c *Checker) instantiateTypeAlias(alias *TypeAlias, m *TypeMapper) *TypeAlias { - if alias == nil { - return nil +func (c *Checker) getTemporaryTypeArguments(n int) []*Type { + if l := len(c.freeTypeArgLists); l > 0 { + s := c.freeTypeArgLists[l-1] + c.freeTypeArgLists[l-1] = nil + c.freeTypeArgLists = c.freeTypeArgLists[:l-1] + if cap(s) >= n { + return s[:n] + } } - return &TypeAlias{symbol: alias.symbol, typeArguments: c.instantiateTypes(alias.typeArguments, m)} + return make([]*Type, n, max(n, 8)) } -func (c *Checker) instantiateTypes(types []*Type, m *TypeMapper) []*Type { - return c.instantiateList(types, m, (*Checker).instantiateType) +func (c *Checker) releaseTemporaryTypeArguments(s []*Type) { + clear(s) + c.freeTypeArgLists = append(c.freeTypeArgLists, s[:0]) +} + +func (c *Checker) getTemporarySymbolList() []*ast.Symbol { + if l := len(c.freeSymbolLists); l > 0 { + s := c.freeSymbolLists[l-1] + c.freeSymbolLists[l-1] = nil + c.freeSymbolLists = c.freeSymbolLists[:l-1] + return s[:0] + } + return make([]*ast.Symbol, 0, 8) } -func (c *Checker) instantiateSymbols(symbols []*ast.Symbol, m *TypeMapper) []*ast.Symbol { - return c.instantiateList(symbols, m, (*Checker).instantiateSymbol) +func (c *Checker) releaseTemporarySymbolList(s []*ast.Symbol) { + if s != nil { + clear(s) + c.freeSymbolLists = append(c.freeSymbolLists, s[:0]) + } } -func (c *Checker) instantiateSignatures(signatures []*Signature, m *TypeMapper) []*Signature { - return c.instantiateList(signatures, m, (*Checker).instantiateSignature) +func (c *Checker) instantiateTypeAlias(alias *TypeAlias, m *TypeMapper) *TypeAlias { + if alias == nil { + return nil + } + newTypeArgs := c.instantiateTypes(alias.typeArguments, m) + if core.Same(newTypeArgs, alias.typeArguments) { + return alias + } + result := c.typeAliasArena.New() + result.symbol = alias.symbol + result.typeArguments = newTypeArgs + return result } -func (c *Checker) instantiateIndexInfos(indexInfos []*IndexInfo, m *TypeMapper) []*IndexInfo { - return c.instantiateList(indexInfos, m, (*Checker).instantiateIndexInfo) +func (c *Checker) instantiateTypesTemp(types []*Type, m *TypeMapper) ([]*Type, bool) { + for i, value := range types { + mapped := c.instantiateType(value, m) + if mapped != value { + result := c.getTemporaryTypeArguments(len(types)) + copy(result, types[:i]) + result[i] = mapped + for j := i + 1; j < len(types); j++ { + result[j] = c.instantiateType(types[j], m) + } + return result, true + } + } + return types, false } -func (c *Checker) instantiateList[T comparable](values []T, m *TypeMapper, instantiator func(c *Checker, value T, m *TypeMapper) T) []T { - for i, value := range values { - mapped := instantiator(c, value, m) +func (c *Checker) instantiateTypes(types []*Type, m *TypeMapper) []*Type { + for i, value := range types { + mapped := c.instantiateType(value, m) if mapped != value { - result := make([]T, len(values)) - copy(result, values[:i]) + result := c.typeListArena.NewSlice(len(types)) + copy(result, types[:i]) result[i] = mapped - for j := i + 1; j < len(values); j++ { - result[j] = instantiator(c, values[j], m) + for j := i + 1; j < len(types); j++ { + result[j] = c.instantiateType(types[j], m) } return result } } - return values + return types +} + +func (c *Checker) instantiateSymbols(symbols []*ast.Symbol, mapper *TypeMapper) []*ast.Symbol { + if len(symbols) != 0 && mapper != nil { + for i, symbol := range symbols { + mapped := c.instantiateSymbol(symbol, mapper) + if symbol != mapped { + result := make([]*ast.Symbol, len(symbols)) + copy(result, symbols[:i]) + result[i] = mapped + for j := i + 1; j < len(symbols); j++ { + result[j] = c.instantiateSymbol(symbols[j], mapper) + } + return result + } + } + } + return symbols +} + +func (c *Checker) instantiateSignatures(signatures []*Signature, mapper *TypeMapper) []*Signature { + if len(signatures) != 0 && mapper != nil { + for i, sig := range signatures { + mapped := c.instantiateSignature(sig, mapper) + if sig != mapped { + result := c.signatureListArena.NewSlice(len(signatures)) + copy(result, signatures[:i]) + result[i] = mapped + for j := i + 1; j < len(signatures); j++ { + result[j] = c.instantiateSignature(signatures[j], mapper) + } + return result + } + } + } + return signatures +} + +func (c *Checker) instantiateIndexInfos(indexInfos []*IndexInfo, mapper *TypeMapper) []*IndexInfo { + if len(indexInfos) != 0 && mapper != nil { + for i, info := range indexInfos { + mapped := c.instantiateIndexInfo(info, mapper) + if info != mapped { + result := c.indexInfoListArena.NewSlice(len(indexInfos)) + copy(result, indexInfos[:i]) + result[i] = mapped + for j := i + 1; j < len(indexInfos); j++ { + result[j] = c.instantiateIndexInfo(indexInfos[j], mapper) + } + return result + } + } + } + return indexInfos } func (c *Checker) tryGetTypeFromTypeNode(node *ast.Node) *Type { @@ -23457,7 +25774,17 @@ func (c *Checker) tryGetTypeFromTypeNode(node *ast.Node) *Type { } func (c *Checker) getTypeFromTypeNode(node *ast.Node) *Type { - return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) + if node.Flags&ast.NodeFlagsCondFlowClean != 0 { + return c.getTypeFromTypeNodeWorker(node) + } + if links := c.typeNodeLinks.TryGet(node); links != nil && links.resolvedFlowType != nil { + return links.resolvedFlowType + } + t := c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) + if node.Flags&ast.NodeFlagsCondFlowClean == 0 && len(c.typeResolutions) == c.resolutionStart { + c.typeNodeLinks.Get(node).resolvedFlowType = t + } + return t } func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { @@ -23587,7 +25914,7 @@ func (c *Checker) getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node *as if links.resolvedType == nil { // Deferred resolution of members is handled by resolveObjectTypeMembers alias := c.getAliasForTypeNode(node) - if sym := node.Symbol(); sym == nil || len(c.getMembersOfSymbol(sym)) == 0 && alias == nil { + if sym := node.Symbol(); sym == nil || alias == nil && !sym.HasLazyMembers() && c.getMembersOfSymbol(sym).Len() == 0 { links.resolvedType = c.emptyTypeLiteralType } else { t := c.newObjectType(ObjectFlagsAnonymous, node.Symbol()) @@ -24019,7 +26346,12 @@ func (c *Checker) createNormalizedTupleTypeEx(target *Type, elementTypes []*Type // In either layout, zero or more generic variadic elements may be present at any location. // Note that the element types may contain an extra 'this' type argument that we want to ignore during normalization // and then just append to the normalized element types. - n := &TupleNormalizer{} + var typesBuf [8]*Type + var infosBuf [8]TupleElementInfo + n := TupleNormalizer{ + types: typesBuf[:0], + infos: infosBuf[:0], + } if !n.normalize(c, elementTypes[:len(d.elementInfos)], d.elementInfos) { return c.errorType } @@ -24031,7 +26363,15 @@ func (c *Checker) createNormalizedTupleTypeEx(target *Type, elementTypes []*Type case tupleTarget == c.emptyGenericType: return c.emptyObjectType case len(n.types) != 0: - return c.createTypeReferenceEx(tupleTarget, n.types, objectFlags) + intf := tupleTarget.AsInterfaceType() + if objectFlags == 0 && slices.Equal(n.types, intf.resolvedTypeArguments) { + return tupleTarget + } + id := getTypeListKey(n.types) + if t := intf.instantiations.get(id); t != nil { + return t + } + return c.createTypeReferenceEx(tupleTarget, c.typeListArena.Clone(n.types), objectFlags) } return tupleTarget } @@ -24270,11 +26610,11 @@ func (c *Checker) getTypeFromTypeAliasReference(node *ast.Node, symbol *ast.Symb if symbol.CheckFlags()&ast.CheckFlagsUnresolved != 0 { alias := &TypeAlias{symbol: symbol, typeArguments: core.Map(typeArguments, c.getTypeFromTypeNode)} key := getAliasKey(alias) - errorType := c.errorTypes[key] + errorType := c.errorTypes.get(key) if errorType == nil { errorType = c.newIntrinsicType(TypeFlagsAny, "error") errorType.alias = alias - c.errorTypes[key] = errorType + c.errorTypes.setSmall(c, key, errorType) } return errorType } @@ -24341,11 +26681,11 @@ func (c *Checker) getTypeAliasInstantiation(symbol *ast.Symbol, typeArguments [] links := c.typeAliasLinks.Get(symbol) typeParameters := links.typeParameters key := getTypeAliasInstantiationKey(typeArguments, alias) - instantiation := links.instantiations[key] + instantiation := links.instantiations.get(key) if instantiation == nil { mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration()))) instantiation = c.instantiateTypeWithAlias(t, mapper, alias) - links.instantiations[key] = instantiation + links.instantiations.setSmall(c, key, instantiation) } return instantiation } @@ -24490,7 +26830,7 @@ func (c *Checker) getOuterTypeParameters(node *ast.Node, includeThisTypes bool) func (c *Checker) getInferTypeParameters(node *ast.Node) []*Type { var result []*Type - for _, symbol := range node.Locals() { + for _, symbol := range node.Locals().All() { if symbol.Flags()&ast.SymbolFlagsTypeParameter != 0 { result = append(result, c.getDeclaredTypeOfSymbol(symbol)) } @@ -24549,8 +26889,7 @@ func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. links.typeParameters = typeParameters - links.instantiations = make(map[CacheHashKey]*Type) - links.instantiations[getTypeListKey(typeParameters)] = t + links.instantiations.setSmall(c, getTypeListKey(typeParameters), t) } if t == c.intrinsicMarkerType && symbol.Name() == "BuiltinIteratorReturn" { t = c.getBuiltinIteratorReturnType() @@ -24758,7 +27097,7 @@ func (c *Checker) evaluateEntity(expr *ast.Node, location *ast.Node) evaluator.R rootSymbol := c.resolveEntityName(root, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil) if rootSymbol != nil && rootSymbol.Flags()&ast.SymbolFlagsEnum != 0 { name := expr.AsElementAccessExpression().ArgumentExpression.Text() - member := rootSymbol.Exports()[name] + member := rootSymbol.Exports().Get(name) if member != nil { if location != nil { return c.evaluateEnumMember(expr, member, location) @@ -24822,17 +27161,46 @@ func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type { } else { links.resolvedType = c.createDeferredTypeReference(target, node, nil /*mapper*/, nil /*alias*/) } - } else { - var elementTypes []*Type - if node.Kind == ast.KindArrayType { - elementTypes = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)} - } else { - elementTypes = core.Map(node.Elements(), c.getTypeFromTypeNode) + } else if node.Kind == ast.KindArrayType { + elemType := c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType) + var buf [1]*Type + buf[0] = elemType + if len(target.AsInterfaceType().resolvedTypeArguments) == 1 && target.AsInterfaceType().resolvedTypeArguments[0] == elemType { + links.resolvedType = target + } else if id := getTypeListKey(buf[:]); true { + if inst := target.AsInterfaceType().instantiations.get(id); inst != nil { + links.resolvedType = inst + } else { + links.resolvedType = c.createTypeReferenceEx(target, c.typeListArena.NewSlice1(elemType), ObjectFlagsFromTypeNode) + } } - if target.objectFlags&ObjectFlagsTuple != 0 { - links.resolvedType = c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode) + } else { + elements := node.Elements() + if target.objectFlags&ObjectFlagsTuple != 0 && target.AsTupleType().combinedFlags&ElementFlagsNonRequired == 0 { + elementTypes := c.getTemporaryTypeArguments(len(elements)) + for i, e := range elements { + elementTypes[i] = c.getTypeFromTypeNode(e) + } + if slices.Equal(elementTypes, target.AsInterfaceType().resolvedTypeArguments) { + c.releaseTemporaryTypeArguments(elementTypes) + links.resolvedType = target + } else if id := getTypeListKey(elementTypes); true { + if inst := target.AsInterfaceType().instantiations.get(id); inst != nil { + c.releaseTemporaryTypeArguments(elementTypes) + links.resolvedType = inst + } else { + cloned := c.typeListArena.Clone(elementTypes) + c.releaseTemporaryTypeArguments(elementTypes) + links.resolvedType = c.createTypeReferenceEx(target, cloned, ObjectFlagsFromTypeNode) + } + } } else { - links.resolvedType = c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode) + elementTypes := core.Map(elements, c.getTypeFromTypeNode) + if target.objectFlags&ObjectFlagsTuple != 0 { + links.resolvedType = c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode) + } else { + links.resolvedType = c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode) + } } } } @@ -24852,7 +27220,18 @@ func (c *Checker) getArrayOrTupleTargetType(node *ast.Node) *Type { } return c.globalArrayType } - return c.getTupleTargetType(core.Map(node.Elements(), c.getTupleElementInfo), readonly) + elements := node.Elements() + var stackBuf [16]TupleElementInfo + var elementInfos []TupleElementInfo + if len(elements) <= len(stackBuf) { + elementInfos = stackBuf[:len(elements)] + } else { + elementInfos = make([]TupleElementInfo, len(elements)) + } + for i, e := range elements { + elementInfos[i] = c.getTupleElementInfo(e) + } + return c.getTupleTargetType(elementInfos, readonly) } func (c *Checker) isReadonlyTypeOperator(node *ast.Node) bool { @@ -24974,7 +27353,7 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { if alias != nil && len(alias.typeArguments) != 0 { outerTypeParameters = allOuterTypeParameters } else { - outerTypeParameters = core.Filter(allOuterTypeParameters, func(tp *Type) bool { return c.isTypeParameterPossiblyReferenced(tp, node) }) + outerTypeParameters = c.filterPossiblyReferencedTypeParameters(allOuterTypeParameters, node) } root := &ConditionalRoot{ node: node.AsConditionalTypeNode(), @@ -24983,13 +27362,11 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { isDistributive: checkType.flags&TypeFlagsTypeParameter != 0, inferTypeParameters: c.getInferTypeParameters(node), outerTypeParameters: outerTypeParameters, - instantiations: nil, alias: alias, } links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil) if outerTypeParameters != nil { - root.instantiations = make(map[CacheHashKey]*Type) - root.instantiations[getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/)] = links.resolvedType + root.instantiations.setSmall(c, getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/), links.resolvedType) } } return links.resolvedType @@ -25004,12 +27381,17 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, // purposes of resolution. We also loop here when resolution of a conditional type ends in resolution of // another (or, through recursion, possibly the same) conditional type. In the potentially tail-recursive // cases we increment the tail recursion counter and stop after 1000 iterations. + maxTailCount := 1000 + if runtime.GOARCH == "wasm" { + maxTailCount = 8192 + } for { - if tailCount == 1000 { + if tailCount == maxTailCount { c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) return c.errorType } - checkType := c.instantiateType(c.getActualTypeVariable(root.checkType), mapper) + actualCheckType := c.getActualTypeVariable(root.checkType) + checkType := c.instantiateType(actualCheckType, mapper) extendsType := c.instantiateType(root.extendsType, mapper) if checkType == c.errorType || extendsType == c.errorType { return c.errorType @@ -25025,74 +27407,116 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, checkTuples := c.isSimpleTupleType(checkTypeNode) && c.isSimpleTupleType(extendsTypeNode) && len(checkTypeNode.Elements()) == len(extendsTypeNode.Elements()) checkTypeDeferred := c.isDeferredType(checkType, checkTuples) var combinedMapper *TypeMapper - if len(root.inferTypeParameters) != 0 { - // When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be - // instantiated with the context, so it doesn't need the mapper for the inference context - however the constraint - // may refer to another _root_, _uncloned_ `infer` type parameter [1], or to something mapped by `mapper` [2]. - // [1] Eg, if we have `Foo` and `Foo` - `B` is constrained to `T`, which, in turn, has been instantiated - // as `number` - // Conversely, if we have `Foo`, `B` is still constrained to `T` and `T` is instantiated as `A` - // [2] Eg, if we have `Foo` and `Foo` where `Q` is mapped by `mapper` into `number` - `B` is constrained to `T` - // which is in turn instantiated as `Q`, which is in turn instantiated as `number`. - // So we need to: - // * combine `context.nonFixingMapper` with `mapper` so their constraints can be instantiated in the context of `mapper` (otherwise they'd only get inference context information) - // * incorporate all of the component mappers into the combined mapper for the true and false members - // This means we have two mappers that need applying: - // * The original `mapper` used to create this conditional - // * The mapper that maps the infer type parameter to its inference result (`context.mapper`) - context := c.newInferenceContext(root.inferTypeParameters, nil /*signature*/, InferenceFlagsNone, nil) - if mapper != nil { - context.nonFixingMapper = c.combineTypeMappers(context.nonFixingMapper, mapper) - } - if !checkTypeDeferred { + if !checkTypeDeferred { + var context *InferenceContext + var nonFixingCombined *TypeMapper + var fixingCombined *TypeMapper + if len(root.inferTypeParameters) != 0 { + // When we're looking at making an inference for an `infer` type, when we get its constraint, it'll automagically be + // instantiated with the context, so it doesn't need the mapper for the inference context - however the constraint + // may refer to another _root_, _uncloned_ `infer` type parameter [1], or to something mapped by `mapper` [2]. + // [1] Eg, if we have `Foo` and `Foo` - `B` is constrained to `T`, which, in turn, has been instantiated + // as `number` + // Conversely, if we have `Foo`, `B` is still constrained to `T` and `T` is instantiated as `A` + // [2] Eg, if we have `Foo` and `Foo` where `Q` is mapped by `mapper` into `number` - `B` is constrained to `T` + // which is in turn instantiated as `Q`, which is in turn instantiated as `number`. + // So we need to: + // * combine `context.nonFixingMapper` with `mapper` so their constraints can be instantiated in the context of `mapper` (otherwise they'd only get inference context information) + // * incorporate all of the component mappers into the combined mapper for the true and false members + // This means we have two mappers that need applying: + // * The original `mapper` used to create this conditional + // * The mapper that maps the infer type parameter to its inference result (`context.mapper`) + context = c.acquireConditionalInferenceContext(root.inferTypeParameters) + if mapper != nil { + nonFixingCombined = c.combineTypeMappers(context.nonFixingMapper, mapper) + context.nonFixingMapper = nonFixingCombined + } // We don't want inferences from constraints as they may cause us to eagerly resolve the // conditional type instead of deferring resolution. Also, we always want strict function // types rules (i.e. proper contravariance) for inferences. c.inferTypes(context.inferences, checkType, extendsType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false) + // It's possible for 'infer T' type parameters to be given uninstantiated constraints when + // those type parameters are used in type references (see getInferredTypeParameterConstraint). For + // that reason we need context.mapper to be first in the combined mapper. See #42636 for examples. + if mapper != nil { + fixingCombined = c.combineTypeMappers(context.mapper, mapper) + combinedMapper = fixingCombined + } else { + combinedMapper = context.mapper + } } - // It's possible for 'infer T' type parameters to be given uninstantiated constraints when the - // those type parameters are used in type references (see getInferredTypeParameterConstraint). For - // that reason we need context.mapper to be first in the combined mapper. See #42636 for examples. - if mapper != nil { - combinedMapper = c.combineTypeMappers(context.mapper, mapper) + // Instantiate the extends type including inferences for 'infer T' type parameters + var inferredExtendsType *Type + if combinedMapper != nil { + inferredExtendsType = c.instantiateType(root.extendsType, combinedMapper) } else { - combinedMapper = context.mapper - } - } - // Instantiate the extends type including inferences for 'infer T' type parameters - var inferredExtendsType *Type - if combinedMapper != nil { - inferredExtendsType = c.instantiateType(root.extendsType, combinedMapper) - } else { - inferredExtendsType = extendsType - } - // We attempt to resolve the conditional type only when the check and extends types are non-generic - if !checkTypeDeferred && !c.isDeferredType(inferredExtendsType, checkTuples) { - // Return falseType for a definitely false extends check. We check an instantiations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instantiations will be and we can just return the false branch type. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 && (checkType.flags&TypeFlagsAny != 0 || !c.isTypeAssignableTo(c.getPermissiveInstantiation(checkType), c.getPermissiveInstantiation(inferredExtendsType))) { - // Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a - // distributive conditional type applied to the constraint of a type variable, include trueType if - // there are possible values of the check type that are also possible values of the extends type. - // We use a reverse assignability check as it is less expensive than the comparable relationship - // and avoids false positives of a non-empty intersection check. - if checkType.flags&TypeFlagsAny != 0 || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(c.getPermissiveInstantiation(inferredExtendsType), func(t *Type) bool { - return c.isTypeAssignableTo(t, c.getPermissiveInstantiation(checkType)) - }) { - extraTypes = append(extraTypes, c.instantiateType(c.getTypeFromTypeNode(root.node.TrueType), core.OrElse(combinedMapper, mapper))) - } - // If falseType is an immediately nested conditional type that isn't distributive or has an - // identical checkType, switch to that type and loop. - falseType := c.getTypeFromTypeNode(root.node.FalseType) - if falseType.flags&TypeFlagsConditional != 0 { - newRoot := falseType.AsConditionalType().root - if newRoot.node.Parent == root.node.AsNode() && (!newRoot.isDistributive || newRoot.checkType == root.checkType) { - root = newRoot - continue + inferredExtendsType = extendsType + } + // We attempt to resolve the conditional type only when the check and extends types are non-generic + if !c.isDeferredType(inferredExtendsType, checkTuples) { + // Return falseType for a definitely false extends check. We check an instantiations of the two + // types with type parameters mapped to the wildcard type, the most permissive instantiations + // possible (the wildcard type is assignable to and from all types). If those are not related, + // then no instantiations will be and we can just return the false branch type. + var permCheck, permExtends *Type + hasPerm := false + if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 { + if checkType.flags&TypeFlagsAny == 0 { + permCheck = c.getPermissiveInstantiation(checkType) + permExtends = c.getPermissiveInstantiation(inferredExtendsType) + hasPerm = true + } + if !hasPerm || !c.isTypeAssignableTo(permCheck, permExtends) { + // Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a + // distributive conditional type applied to the constraint of a type variable, include trueType if + // there are possible values of the check type that are also possible values of the extends type. + // We use a reverse assignability check as it is less expensive than the comparable relationship + // and avoids false positives of a non-empty intersection check. + if !hasPerm || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(permExtends, func(t *Type) bool { + return c.isTypeAssignableTo(t, permCheck) + }) { + extraTypes = append(extraTypes, c.instantiateType(c.getTypeFromTypeNode(root.node.TrueType), core.OrElse(combinedMapper, mapper))) + } + if context != nil && !context.escaped { + c.releaseConditionalInferenceContext(context, fixingCombined, nonFixingCombined) + } + // If falseType is an immediately nested conditional type that isn't distributive or has an + // identical checkType, switch to that type and loop. + falseType := c.getTypeFromTypeNode(root.node.FalseType) + if falseType.flags&TypeFlagsConditional != 0 { + newRoot := falseType.AsConditionalType().root + if newRoot.node.Parent == root.node.AsNode() && (!newRoot.isDistributive || newRoot.checkType == root.checkType) { + root = newRoot + continue + } + if newRoot, newRootMapper := c.getTailRecursionRoot(falseType, mapper); newRoot != nil { + root = newRoot + mapper = newRootMapper + alias = nil + if newRoot.alias != nil { + tailCount++ + } + continue + } + } + result = c.instantiateType(falseType, mapper) + break } - if newRoot, newRootMapper := c.getTailRecursionRoot(falseType, mapper); newRoot != nil { + } + // Return trueType for a definitely true extends check. We check instantiations of the two + // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter + // that has no constraint. This ensures that, for example, the type + // type Foo = T extends { x: string } ? string : number + // doesn't immediately resolve to 'string' instead of being deferred. + if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || + hasPerm && permCheck == checkType && permExtends == inferredExtendsType && c.isNonGenericForInstantiation(checkType) && c.isNonGenericForInstantiation(inferredExtendsType) || + c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(inferredExtendsType)) { + trueType := c.getTypeFromTypeNode(root.node.TrueType) + trueMapper := core.OrElse(combinedMapper, mapper) + if newRoot, newRootMapper := c.getTailRecursionRoot(trueType, trueMapper); newRoot != nil { + if context != nil && !context.escaped { + c.releaseConditionalInferenceContext(context, fixingCombined, nonFixingCombined) + } root = newRoot mapper = newRootMapper alias = nil @@ -25101,33 +27525,19 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, } continue } - } - result = c.instantiateType(falseType, mapper) - break - } - // Return trueType for a definitely true extends check. We check instantiations of the two - // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter - // that has no constraint. This ensures that, for example, the type - // type Foo = T extends { x: string } ? string : number - // doesn't immediately resolve to 'string' instead of being deferred. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(inferredExtendsType)) { - trueType := c.getTypeFromTypeNode(root.node.TrueType) - trueMapper := core.OrElse(combinedMapper, mapper) - if newRoot, newRootMapper := c.getTailRecursionRoot(trueType, trueMapper); newRoot != nil { - root = newRoot - mapper = newRootMapper - alias = nil - if newRoot.alias != nil { - tailCount++ + result = c.instantiateType(trueType, trueMapper) + if context != nil && !context.escaped { + c.releaseConditionalInferenceContext(context, fixingCombined, nonFixingCombined) } - continue + break } - result = c.instantiateType(trueType, trueMapper) - break } } - // Return a deferred type for a check that is neither definitely true nor definitely false - result = c.newConditionalType(root, mapper, combinedMapper) + rawCheckType := checkType + if actualCheckType != root.checkType { + rawCheckType = c.instantiateType(root.checkType, mapper) + } + result = c.newConditionalType(root, rawCheckType, extendsType, mapper, combinedMapper) if alias != nil { result.alias = alias } else { @@ -25149,9 +27559,40 @@ func (c *Checker) getTailRecursionRoot(newType *Type, newMapper *TypeMapper) (*C if newType.flags&TypeFlagsConditional != 0 && newMapper != nil { newRoot := newType.AsConditionalType().root if len(newRoot.outerTypeParameters) != 0 { - typeParamMapper := c.combineTypeMappers(newType.AsConditionalType().mapper, newMapper) - typeArguments := core.Map(newRoot.outerTypeParameters, typeParamMapper.Map) - newRootMapper := newTypeMapper(newRoot.outerTypeParameters, typeArguments) + m1 := newType.AsConditionalType().mapper + var newRootMapper *TypeMapper + if m1 == nil && (newMapper.kind == TypeMapperKindSimple || newMapper.kind == TypeMapperKindArray) { + newRootMapper = newMapper + } else if len(newRoot.outerTypeParameters) == 1 { + tp := newRoot.outerTypeParameters[0] + var arg *Type + if m1 != nil { + if t1 := m1.Map(tp); t1 != tp { + arg = c.instantiateType(t1, newMapper) + } else { + arg = newMapper.Map(tp) + } + } else { + arg = newMapper.Map(tp) + } + newRootMapper = newSimpleTypeMapper(tp, arg) + } else { + typeArguments := c.typeListArena.NewSlice(len(newRoot.outerTypeParameters)) + if m1 != nil { + for i, tp := range newRoot.outerTypeParameters { + if t1 := m1.Map(tp); t1 != tp { + typeArguments[i] = c.instantiateType(t1, newMapper) + } else { + typeArguments[i] = newMapper.Map(tp) + } + } + } else { + for i, tp := range newRoot.outerTypeParameters { + typeArguments[i] = newMapper.Map(tp) + } + } + newRootMapper = newArrayTypeMapper(newRoot.outerTypeParameters, typeArguments) + } var newCheckType *Type if newRoot.isDistributive { newCheckType = getMappedType(newRoot.checkType, newRootMapper) @@ -25174,8 +27615,13 @@ func (c *Checker) isDeferredType(t *Type, checkTuples bool) bool { return c.isGenericType(t) || checkTuples && isTupleType(t) && core.Some(c.getElementTypes(t), c.isGenericType) } +func (c *Checker) isNonGenericForInstantiation(t *Type) bool { + return t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 || + !c.couldContainTypeVariables(t) && (t.alias == nil || len(t.alias.typeArguments) == 0 || !c.anyCouldContainTypeVariables(t.alias.typeArguments)) +} + func (c *Checker) getPermissiveInstantiation(t *Type) *Type { - if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 { + if c.isNonGenericForInstantiation(t) { return t } key := CachedTypeKey{kind: CachedTypeKindPermissiveInstantiation, typeId: t.id} @@ -25188,7 +27634,8 @@ func (c *Checker) getPermissiveInstantiation(t *Type) *Type { } func (c *Checker) getRestrictiveInstantiation(t *Type) *Type { - if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 { + if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 || + !c.couldContainTypeVariables(t) && (t.alias == nil || len(t.alias.typeArguments) == 0 || !c.anyCouldContainTypeVariables(t.alias.typeArguments)) { return t } key := CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id} @@ -25236,30 +27683,51 @@ func (c *Checker) permissiveMapperWorker(t *Type) *Type { func (c *Checker) getTrueTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedTrueType == nil { - d.resolvedTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper) + ex := d.ensureExtra(c) + if ex.resolvedTrueType == nil { + ex.resolvedTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper) } - return d.resolvedTrueType + return ex.resolvedTrueType } func (c *Checker) getFalseTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedFalseType == nil { - d.resolvedFalseType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper) + ex := d.ensureExtra(c) + if ex.resolvedFalseType == nil { + ex.resolvedFalseType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper) } - return d.resolvedFalseType + return ex.resolvedFalseType } func (c *Checker) getInferredTrueTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedInferredTrueType == nil { - if d.combinedMapper != nil { - d.resolvedInferredTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.combinedMapper) + ex := d.ensureExtra(c) + if ex.resolvedInferredTrueType == nil { + combinedMapper := ex.combinedMapper + var context *InferenceContext + var nonFixingCombined *TypeMapper + var fixingCombined *TypeMapper + if combinedMapper == nil && len(d.root.inferTypeParameters) != 0 { + context = c.acquireConditionalInferenceContext(d.root.inferTypeParameters) + if d.mapper != nil { + context.nonFixingMapper = c.combineTypeMappers(context.nonFixingMapper, d.mapper) + fixingCombined = c.combineTypeMappers(context.mapper, d.mapper) + nonFixingCombined = context.nonFixingMapper + combinedMapper = fixingCombined + } else { + combinedMapper = context.mapper + } + } + if combinedMapper != nil { + ex.resolvedInferredTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), combinedMapper) + if context != nil && !context.escaped { + c.releaseConditionalInferenceContext(context, fixingCombined, nonFixingCombined) + } } else { - d.resolvedInferredTrueType = c.getTrueTypeFromConditionalType(t) + ex.resolvedInferredTrueType = c.getTrueTypeFromConditionalType(t) } } - return d.resolvedInferredTrueType + return ex.resolvedInferredTrueType } func (c *Checker) getTypeFromInferTypeNode(node *ast.Node) *Type { @@ -25401,7 +27869,19 @@ func (c *Checker) createArrayType(elementType *Type) *Type { } func (c *Checker) createArrayTypeEx(elementType *Type, readonly bool) *Type { - return c.createTypeFromGenericGlobalType(core.IfElse(readonly, c.globalReadonlyArrayType, c.globalArrayType), []*Type{elementType}) + genericGlobalType := core.IfElse(readonly, c.globalReadonlyArrayType, c.globalArrayType) + if genericGlobalType == c.emptyGenericType { + return c.emptyObjectType + } + intf := genericGlobalType.AsInterfaceType() + if len(intf.resolvedTypeArguments) == 1 && intf.resolvedTypeArguments[0] == elementType { + return genericGlobalType + } + id := packedSingleTypeKey(packedInstantiationKeyTag, elementType, false) + if t := intf.instantiations.get(id); t != nil { + return t + } + return c.createTypeReference(genericGlobalType, c.typeListArena.NewSlice1(elementType)) } func (c *Checker) getTupleElementFlags(node *ast.Node) ElementFlags { @@ -25431,7 +27911,16 @@ func (c *Checker) getTupleElementInfo(node *ast.Node) TupleElementInfo { } func (c *Checker) createTupleType(elementTypes []*Type) *Type { - elementInfos := core.Map(elementTypes, func(_ *Type) TupleElementInfo { return TupleElementInfo{flags: ElementFlagsRequired} }) + var stackBuf [16]TupleElementInfo + var elementInfos []TupleElementInfo + if len(elementTypes) <= len(stackBuf) { + elementInfos = stackBuf[:len(elementTypes)] + } else { + elementInfos = make([]TupleElementInfo, len(elementTypes)) + } + for i := range elementInfos { + elementInfos[i] = TupleElementInfo{flags: ElementFlagsRequired} + } return c.createTupleTypeEx(elementTypes, elementInfos, false /*readonly*/) } @@ -25455,10 +27944,10 @@ func (c *Checker) getTupleTargetType(elementInfos []TupleElementInfo, readonly b return c.globalArrayType } key := getTupleKey(elementInfos, readonly) - t := c.tupleTypes[key] + t := c.tupleTypes.get(key) if t == nil { t = c.createTupleTargetType(elementInfos, readonly) - c.tupleTypes[key] = t + c.tupleTypes.setSmall(c, key, t) } return t } @@ -25476,7 +27965,7 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl return e.flags&(ElementFlagsRequired|ElementFlagsVariadic) != 0 }) var typeParameters []*Type - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() combinedFlags := ElementFlagsNone if arity != 0 { typeParameters = make([]*Type, 0, arity) @@ -25489,11 +27978,11 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl property := c.newSymbolEx(ast.SymbolFlagsProperty|core.IfElse(flags&ElementFlagsOptional != 0, ast.SymbolFlagsOptional, 0), strconv.Itoa(i), core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) c.valueSymbolLinks.Get(property).resolvedType = typeParameter // c.valueSymbolLinks.get(property).tupleLabelDeclaration = elementInfos[i].labeledDeclaration - members[property.Name()] = property + members.Set(property.Name(), property) } } } - fixedLength := len(members) + fixedLength := members.Len() lengthSymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "length", core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) if combinedFlags&ElementFlagsVariable != 0 { c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.numberType @@ -25504,20 +27993,18 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl } c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.getUnionType(literalTypes) } - members[lengthSymbol.Name()] = lengthSymbol + members.Set(lengthSymbol.Name(), lengthSymbol) t := c.newObjectType(ObjectFlagsTuple|ObjectFlagsReference, nil) d := t.AsTupleType() d.thisType = c.newTypeParameter(nil) d.thisType.AsTypeParameter().isThisType = true d.thisType.AsTypeParameter().constraint = t d.allTypeParameters = append(typeParameters, d.thisType) - d.instantiations = make(map[CacheHashKey]*Type) - d.instantiations[getTypeListKey(d.TypeParameters())] = t d.target = t d.resolvedTypeArguments = d.TypeParameters() d.declaredMembersResolved = true d.declaredMembers = members - d.elementInfos = elementInfos + d.elementInfos = slices.Clone(elementInfos) d.minLength = minLength d.fixedLength = fixedLength d.combinedFlags = combinedFlags @@ -25593,7 +28080,7 @@ func (c *Checker) getGenericObjectFlags(t *Type) ObjectFlags { if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) || c.isGenericTupleType(t) { combinedFlags |= ObjectFlagsIsGenericObjectType } - if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsIndex) != 0 || c.isGenericStringLikeType(t) { + if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || t.flags&TypeFlagsIndex != 0 && !c.isBinaryKeyedTypeLiteral(t.AsIndexType().target) || c.isGenericStringLikeType(t) { combinedFlags |= ObjectFlagsIsGenericIndexType } return combinedFlags @@ -25633,11 +28120,16 @@ func (c *Checker) isGenericReducibleType(t *Type) bool { } func (c *Checker) isReducibleIntersection(t *Type) bool { - d := t.AsIntersectionType() - if d.uniqueLiteralFilledInstantiation == nil { - d.uniqueLiteralFilledInstantiation = c.instantiateType(t, c.uniqueLiteralMapper) + if t.cacheFlags&typeCacheIsReducibleIntersectionComputed == 0 { + inst := c.instantiateType(t, c.uniqueLiteralMapper) + if c.getReducedType(inst) != inst { + t.cacheFlags |= typeCacheIsReducibleIntersectionComputed | typeCacheIsReducibleIntersection + return true + } + t.cacheFlags |= typeCacheIsReducibleIntersectionComputed + return false } - return c.getReducedType(d.uniqueLiteralFilledInstantiation) != d.uniqueLiteralFilledInstantiation + return t.cacheFlags&typeCacheIsReducibleIntersection != 0 } func (c *Checker) getUniqueLiteralTypeForTypeParameter(t *Type) *Type { @@ -25647,10 +28139,35 @@ func (c *Checker) getUniqueLiteralTypeForTypeParameter(t *Type) *Type { return t } +func condFlowRelevant(node *ast.Node) bool { + parent := node.Parent + if parent == nil { + return false + } + if ast.IsParameterDeclaration(parent) { + return true + } + if ast.IsConditionalTypeNode(parent) && node == parent.AsConditionalTypeNode().TrueType { + return true + } + if ast.IsMappedTypeNode(parent) && parent.AsMappedTypeNode().NameType == nil && node == parent.Type() { + return true + } + return false +} + func (c *Checker) getConditionalFlowTypeOfType(t *Type, node *ast.Node) *Type { var constraints []*Type covariant := true + start := node + var lastRelevant *ast.Node for node != nil && !ast.IsStatement(node) && node.Kind != ast.KindJSDoc { + if node.Flags&ast.NodeFlagsCondFlowClean != 0 { + break + } + if condFlowRelevant(node) { + lastRelevant = node + } parent := node.Parent // only consider variance flipped by parameter locations - `keyof` types would usually be considered variance inverting, but // often get used in indexed accesses where they behave sortof invariantly, but our checking is lax @@ -25678,6 +28195,14 @@ func (c *Checker) getConditionalFlowTypeOfType(t *Type, node *ast.Node) *Type { } node = parent } + mark := start + if lastRelevant != nil { + mark = lastRelevant.Parent + } + for mark != node && mark != nil { + mark.Flags |= ast.NodeFlagsCondFlowClean + mark = mark.Parent + } if len(constraints) != 0 { return c.getSubstitutionType(t, c.getIntersectionType(constraints)) } @@ -25702,6 +28227,9 @@ func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, t *Type, kin c.TypeCount++ t.flags = flags t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved) + if flags&TypeFlagsStructuredOrInstantiable == 0 { + t.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed + } t.id = TypeId(c.TypeCount) t.kind = kind t.checker = c @@ -25759,36 +28287,26 @@ func (c *Checker) newUniqueESSymbolType(symbol *ast.Symbol, name string) *Type { func (c *Checker) newObjectType(objectFlags ObjectFlags, symbol *ast.Symbol) *Type { var t *Type - var kind typeKind switch { case objectFlags&ObjectFlagsClassOrInterface != 0: - t = c.interfaceTypeArena.New().AsType() - kind = typeKindInterface + t = c.newType(TypeFlagsObject, objectFlags, c.interfaceTypeArena.New().AsType(), typeKindInterface) case objectFlags&ObjectFlagsTuple != 0: - t = (&TupleType{}).AsType() - kind = typeKindTuple + t = c.newType(TypeFlagsObject, objectFlags, (&TupleType{}).AsType(), typeKindTuple) case objectFlags&ObjectFlagsReference != 0: - t = c.typeReferenceArena.New().AsType() - kind = typeKindTypeReference + t = c.newType(TypeFlagsObject, objectFlags, c.typeReferenceArena.New().AsType(), typeKindTypeReference) case objectFlags&ObjectFlagsMapped != 0: - t = c.mappedTypeArena.New().AsType() - kind = typeKindMapped + t = c.newType(TypeFlagsObject, objectFlags, c.mappedTypeArena.New().AsType(), typeKindMapped) case objectFlags&ObjectFlagsReverseMapped != 0: - t = (&ReverseMappedType{}).AsType() - kind = typeKindReverseMapped + t = c.newType(TypeFlagsObject, objectFlags, (&ReverseMappedType{}).AsType(), typeKindReverseMapped) case objectFlags&ObjectFlagsEvolvingArray != 0: - t = (&EvolvingArrayType{}).AsType() - kind = typeKindEvolvingArray + t = c.newType(TypeFlagsObject, objectFlags, (&EvolvingArrayType{}).AsType(), typeKindEvolvingArray) case objectFlags&ObjectFlagsInstantiationExpressionType != 0: - t = (&InstantiationExpressionType{}).AsType() - kind = typeKindInstantiationExpression + t = c.newType(TypeFlagsObject, objectFlags, (&InstantiationExpressionType{}).AsType(), typeKindInstantiationExpression) case objectFlags&ObjectFlagsAnonymous != 0: - t = c.objectTypeArena.New().AsType() - kind = typeKindObject + t = c.newType(TypeFlagsObject, objectFlags, c.objectTypeArena.New().AsType(), typeKindObject) default: panic("Unhandled case in newObjectType") } - c.newType(TypeFlagsObject, objectFlags, t, kind) t.symbol = symbol return t } @@ -25811,16 +28329,19 @@ func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type } func (c *Checker) createTypeReferenceEx(target *Type, typeArguments []*Type, objectFlags ObjectFlags) *Type { - id := getTypeListKey(typeArguments) intf := target.AsInterfaceType() - if t, ok := intf.instantiations[id]; ok { + if objectFlags == 0 && slices.Equal(typeArguments, intf.resolvedTypeArguments) { + return target + } + id := getTypeListKey(typeArguments) + if t := intf.instantiations.get(id); t != nil { return t } t := c.newObjectType(ObjectFlagsReference|objectFlags|c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone), target.symbol) d := t.AsTypeReference() d.target = target d.resolvedTypeArguments = typeArguments - intf.instantiations[id] = t + intf.setInstantiation(c, id, t) return t } @@ -25836,6 +28357,7 @@ func (c *Checker) createDeferredTypeReference(target *Type, node *ast.Node, mapp d := t.AsTypeReference() d.target = target d.mapper = mapper + markMapperEscaped(mapper) d.node = node return t } @@ -25851,13 +28373,27 @@ func (c *Checker) cloneTypeReference(source *Type) *Type { func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { t.objectFlags |= ObjectFlagsMembersResolved data := t.AsStructuredType() - data.members = members - data.properties = c.getNamedMembers(members, t.symbol) + // Named-member filtering may recursively query this type before it finishes. + var mIdx *memberIndex + if members.Len() != 0 || (members != nil && t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsValueModule != 0) { + mIdx = c.memberIndexArena.New() + mIdx.fallback = members + data.setMembersAndProperties(mIdx, nil) + } else if data.extra != nil { + data.extra.members = nil + } + properties := c.getNamedMembers(members, t.symbol) + if mIdx != nil && t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsValueModule != 0 { + mIdx.fallback = members + } else { + mIdx = c.newMemberIndex(members, properties, mIdx) + } + data.setMembersAndProperties(mIdx, properties) c.setStructuredTypeSignatures(data, callSignatures, constructSignatures, indexInfos) } func (c *Checker) setStructuredTypeSignatures(data *StructuredType, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { - if data.extra == nil && callSignatures == nil && constructSignatures == nil && indexInfos == nil { + if data.extra == nil && len(callSignatures) == 0 && len(constructSignatures) == 0 && len(indexInfos) == 0 { return } extra := data.ensureExtra() @@ -25941,14 +28477,19 @@ func (c *Checker) newStringMappingType(symbol *ast.Symbol, target *Type) *Type { return t } -func (c *Checker) newConditionalType(root *ConditionalRoot, mapper *TypeMapper, combinedMapper *TypeMapper) *Type { +func (c *Checker) newConditionalType(root *ConditionalRoot, checkType *Type, extendsType *Type, mapper *TypeMapper, combinedMapper *TypeMapper) *Type { data := c.conditionalTypeArena.New() data.root = root - data.checkType = c.instantiateType(root.checkType, mapper) - data.extendsType = c.instantiateType(root.extendsType, mapper) + data.checkType = checkType + data.extendsType = extendsType data.mapper = mapper - data.combinedMapper = combinedMapper - return c.newType(TypeFlagsConditional, ObjectFlagsNone, data.AsType(), typeKindConditional) + t := c.newType(TypeFlagsConditional, ObjectFlagsNone, data.AsType(), typeKindConditional) + if combinedMapper != nil { + t.AsConditionalType().ensureExtra(c).combinedMapper = combinedMapper + } + markMapperEscaped(mapper) + markMapperEscaped(combinedMapper) + return t } func (c *Checker) newSubstitutionType(baseType *Type, constraint *Type) *Type { @@ -25966,7 +28507,9 @@ func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, type sig.declaration = declaration sig.setTypeParameters(c, typeParameters) sig.parameters = parameters - sig.setThisParameter(c, thisParameter) + if thisParameter != nil { + sig.ensureExtra(c).thisParameter = thisParameter + } sig.resolvedReturnType = resolvedReturnType sig.setResolvedTypePredicate(c, resolvedTypePredicate) sig.minArgumentCount = int32(minArgumentCount) @@ -25989,11 +28532,11 @@ func (c *Checker) getRegularTypeOfLiteralType(t *Type) *Type { return t.AsLiteralType().regularType } if t.flags&TypeFlagsUnion != 0 { - u := t.AsUnionType() - if u.regularType == nil { - u.regularType = c.mapType(t, c.getRegularTypeOfLiteralType) + ex := t.AsStructuredType().ensureExtra() + if ex.regularType == nil { + ex.regularType = c.mapType(t, c.getRegularTypeOfLiteralType) } - return u.regularType + return ex.regularType } return t } @@ -26289,11 +28832,6 @@ func (c *Checker) mapTypeEx(t *Type, f func(*Type) *Type, noReductions bool) *Ty types = u.origin.Types() } var set *temporaryTypeSet - defer func() { - if set != nil { - c.releaseTemporaryTypeSet(set) - } - }() var mappedTypes []*Type var changed bool for i, s := range types { @@ -26316,9 +28854,12 @@ func (c *Checker) mapTypeEx(t *Type, f func(*Type) *Type, noReductions bool) *Ty if changed { set.values = mappedTypes if len(mappedTypes) == 0 { + c.releaseTemporaryTypeSet(set) return nil } - return c.getUnionTypeEx(mappedTypes, core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil /*alias*/, nil /*origin*/) + res := c.getUnionTypeEx(mappedTypes, core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil /*alias*/, nil /*origin*/) + c.releaseTemporaryTypeSet(set) + return res } return t } @@ -26349,6 +28890,81 @@ func (c *Checker) getUnionType(types []*Type) *Type { // expression constructs such as array literals and the || and ?: operators). Named types can // circularly reference themselves and therefore cannot be subtype reduced during their declaration. // For example, "type Item = string | (() => Item" is a named type that circularly references itself. +const unionFrontMaxWords = 8 + +type unionFrontSlot struct { + key [unionFrontMaxWords]uint32 + result *Type + meta uint16 +} + +func makeUnionFrontSlotKey(types []*Type, unionReduction UnionReduction, alias *TypeAlias) ([unionFrontMaxWords]uint32, uint16, uint32, bool) { + var key [unionFrontMaxWords]uint32 + hasAlias := alias != nil + extra := 0 + var aliasSymID uint64 + if hasAlias { + aliasSymID = uint64(ast.GetSymbolId(alias.symbol)) + if aliasSymID >= (1 << 32) { + return key, 0, 0, false + } + extra = 1 + len(alias.typeArguments) + } + if len(types)+extra > unionFrontMaxWords { + return key, 0, 0, false + } + var h uint64 = uint64(unionReduction) + n := 0 + for _, t := range types { + w := uint32(t.id) + key[n] = w + n++ + h = (h ^ uint64(w)) * 0x9e3779b97f4a7c15 + } + if hasAlias { + w := uint32(aliasSymID) + key[n] = w + n++ + h = (h ^ uint64(w)) * 0x9e3779b97f4a7c15 + for _, t := range alias.typeArguments { + w := uint32(t.id) + key[n] = w + n++ + h = (h ^ uint64(w)) * 0x9e3779b97f4a7c15 + } + } + h ^= h >> 29 + meta := uint16(1) | (uint16(unionReduction) << 1) | (uint16(len(types)) << 3) + if hasAlias { + meta |= 1 << 7 + } + h ^= uint64(meta) + h *= 0x9e3779b97f4a7c15 + return key, meta, uint32(h >> (64 - 10)), true +} + +func getUnionFrontKey(types []*Type, unionReduction UnionReduction, alias *TypeAlias) CacheHashKey { + if key, ok := packedTypeListKeyWithAlias(packedUnionFrontKeyTag|uint64(unionReduction), types, alias, false); ok { + return key + } + return getUnionFrontKeySlow(types, unionReduction, alias) +} + +func getUnionFrontKeySlow(types []*Type, unionReduction UnionReduction, alias *TypeAlias) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + b.writeUint32(uint32(unionReduction)) + b.writeTypes(types) + b.writeAlias(alias) + return b.releaseHash() + } + var b keyBuilder + b.writeUint32(uint32(unionReduction)) + b.writeTypes(types) + b.writeAlias(alias) + return b.hash() +} + func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, alias *TypeAlias, origin *Type) *Type { if len(types) == 0 { return c.neverType @@ -26356,7 +28972,7 @@ func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, a if len(types) == 1 { return types[0] } - // We optimize for the common case of unioning a union type with some other type (such as `undefined`). + // We optimize for the common case of unioning a union type with some other type (such as 'undefined'). if len(types) == 2 && origin == nil && (types[0].flags&TypeFlagsUnion != 0 || types[1].flags&TypeFlagsUnion != 0) { id1 := types[0].id id2 := types[1].id @@ -26371,6 +28987,50 @@ func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, a } return t } + if origin == nil && (alias == nil || alias.symbol != nil) { + if slotKey, meta, idx, ok := makeUnionFrontSlotKey(types, unionReduction, alias); ok { + if c.unionFrontSlots != nil { + slot := &c.unionFrontSlots[idx] + if slot.meta == meta && slot.key == slotKey { + return slot.result + } + } + typeCount := c.TypeCount + instCount := c.instantiationCount + totalInstCount := c.TotalInstantiationCount + impure := c.unionImpureCount + result := c.getUnionTypeWorker(types, unionReduction, alias, nil) + created := c.TypeCount != typeCount && !(c.TypeCount == typeCount+1 && result.id == TypeId(c.TypeCount)) + instantiated := c.instantiationCount != instCount || c.TotalInstantiationCount != totalInstCount + wasImpure := c.unionImpureCount != impure + if !created && !instantiated && !wasImpure && result != c.errorType { + if c.unionFrontSlots == nil { + c.unionFrontSlots = new([1024]unionFrontSlot) + } + c.unionFrontSlots[idx] = unionFrontSlot{key: slotKey, result: result, meta: meta} + } + return result + } + key := getUnionFrontKey(types, unionReduction, alias) + if cached := c.unionFrontCache[key]; cached != nil { + return cached + } + typeCount := c.TypeCount + instCount := c.instantiationCount + totalInstCount := c.TotalInstantiationCount + impure := c.unionImpureCount + result := c.getUnionTypeWorker(types, unionReduction, alias, nil) + created := c.TypeCount != typeCount && !(c.TypeCount == typeCount+1 && result.id == TypeId(c.TypeCount)) + instantiated := c.instantiationCount != instCount || c.TotalInstantiationCount != totalInstCount + wasImpure := c.unionImpureCount != impure + if !created && !instantiated && !wasImpure && result != c.errorType { + if c.unionFrontCache == nil { + c.unionFrontCache = make(map[CacheHashKey]*Type) + } + c.unionFrontCache[key] = result + } + return result + } return c.getUnionTypeWorker(types, unionReduction, alias, origin) } @@ -26403,9 +29063,11 @@ func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReductio typeSet = c.removeRedundantLiteralTypes(typeSet, includes, unionReduction&UnionReductionSubtype != 0) } if includes&TypeFlagsStringLiteral != 0 && includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 { + c.unionImpureCount++ typeSet = c.removeStringLiteralsMatchedByTemplateLiterals(typeSet) } if includes&TypeFlagsIncludesConstrainedTypeVariable != 0 { + c.unionImpureCount++ typeSet = c.removeConstrainedTypeVariables(typeSet) } if unionReduction == UnionReductionSubtype { @@ -26431,15 +29093,36 @@ func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReductio } } if origin == nil && includes&TypeFlagsUnion != 0 { - namedUnions := c.addNamedUnions(nil, types) - var reducedTypes []*Type - for _, t := range typeSet { - if !core.Some(namedUnions, func(u *Type) bool { return containsType(u.Types(), t) }) { - reducedTypes = append(reducedTypes, t) + namedUnions := c.addNamedUnions(c.getTemporaryTypeArguments(0)[:0], types) + reducedTypes := c.getTemporaryTypeArguments(0)[:0] + if len(c.files) > 0 && (len(namedUnions) > 1 || len(typeSet) > 8) { + if c.namedUnionMemberSet == nil || len(c.namedUnionMemberSet) > 64 { + c.namedUnionMemberSet = make(map[*Type]struct{}, 64) + } else if len(c.namedUnionMemberSet) != 0 { + clear(c.namedUnionMemberSet) + } + for _, u := range namedUnions { + for _, s := range u.Types() { + c.namedUnionMemberSet[s] = struct{}{} + } + } + for _, t := range typeSet { + if _, ok := c.namedUnionMemberSet[t]; !ok { + reducedTypes = append(reducedTypes, t) + } + } + } else { + for _, t := range typeSet { + if !core.Some(namedUnions, func(u *Type) bool { return containsType(u.Types(), t) }) { + reducedTypes = append(reducedTypes, t) + } } } if alias == nil && len(namedUnions) == 1 && len(reducedTypes) == 0 { - return namedUnions[0] + res := namedUnions[0] + c.releaseTemporaryTypeArguments(namedUnions) + c.releaseTemporaryTypeArguments(reducedTypes) + return res } // We create a denormalized origin type only when the union was created from one or more named unions // (unions with alias symbols or origins) and when there is no overlap between those named unions. @@ -26453,6 +29136,8 @@ func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReductio } origin = c.newUnionType(ObjectFlagsNone, reducedTypes) } + c.releaseTemporaryTypeArguments(namedUnions) + c.releaseTemporaryTypeArguments(reducedTypes) } objectFlags := core.IfElse(includes&TypeFlagsNotPrimitiveUnion != 0, ObjectFlagsNone, ObjectFlagsPrimitiveUnion) | core.IfElse(includes&TypeFlagsIntersection != 0, ObjectFlagsContainsIntersections, ObjectFlagsNone) @@ -26468,25 +29153,47 @@ func (c *Checker) getUnionTypeFromSortedList(types []*Type, precomputedObjectFla return types[0] } key := getUnionKey(types, origin, alias) - t := c.unionTypes[key] + t := c.unionTypes.get(key) if t == nil { - t = c.newUnionType(precomputedObjectFlags|c.getPropagatingFlagsOfTypes(types, TypeFlagsNullable), types) + var propagatingFlags ObjectFlags + nonNullable := true + for _, elem := range types { + if elem.flags&TypeFlagsNullable == 0 { + propagatingFlags |= elem.objectFlags + } + if elem.flags&(TypeFlagsDefinitelyNonNullable&^TypeFlagsInstantiable) == 0 { + nonNullable = false + } + } + t = c.newUnionType(precomputedObjectFlags|(propagatingFlags&ObjectFlagsPropagatingFlags), types) + if nonNullable { + t.cacheFlags |= typeCacheNonNullableUnion + } t.AsUnionType().origin = origin t.alias = alias if len(types) == 2 && types[0].flags&TypeFlagsBooleanLiteral != 0 && types[1].flags&TypeFlagsBooleanLiteral != 0 { t.flags |= TypeFlagsBoolean } - c.unionTypes[key] = t + c.unionTypes.set(c, key, t) } return t } func (c *Checker) UnionTypes() iter.Seq[*Type] { - return maps.Values(c.unionTypes) + return func(yield func(*Type) bool) { + for i := range c.unionTypes.slots { + if v := c.unionTypes.slots[i].val; v != nil { + if !yield(v) { + return + } + } + } + } } func (c *Checker) addTypesToUnion(sourceTypes []*Type, types []*Type) ([]*Type, TypeFlags) { var includes TypeFlags + totalOrder := len(c.files) > 0 addType := func(t *Type) { flags := t.flags // We ignore 'never' types in unions @@ -26514,9 +29221,31 @@ func (c *Checker) addTypesToUnion(sourceTypes []*Type, types []*Type) ([]*Type, } types = append(types, t) } + if totalOrder && len(sourceTypes) == 2 && (sourceTypes[0].flags&sourceTypes[1].flags)&TypeFlagsUnion != 0 { + if sourceTypes[0] == sourceTypes[1] { + u := sourceTypes[0].AsUnionType() + if sourceTypes[0].alias != nil || u.origin != nil { + includes |= TypeFlagsUnion + } + for _, s := range u.types { + addType(s) + } + return types, includes + } + for _, t := range sourceTypes { + u := t.AsUnionType() + if t.alias != nil || u.origin != nil { + includes |= TypeFlagsUnion + } + } + mergeSortedTypes(sourceTypes[0].AsUnionType().types, sourceTypes[1].AsUnionType().types, true /*dedup*/, addType) + return types, includes + } + runStarts := c.unionRunStarts[:0] var lastType *Type for _, t := range sourceTypes { if t != lastType { + start := len(types) if t.flags&TypeFlagsUnion != 0 { u := t.AsUnionType() if t.alias != nil || u.origin != nil { @@ -26528,12 +29257,20 @@ func (c *Checker) addTypesToUnion(sourceTypes []*Type, types []*Type) ([]*Type, } else { addType(t) } + if totalOrder && start > 0 && len(types) > start && CompareTypes(types[start-1], types[start]) > 0 { + runStarts = append(runStarts, start) + } lastType = t } } + c.unionRunStarts = runStarts if len(types) >= 2 { // Sort and deduplicate types - slices.SortStableFunc(types, CompareTypes) + if !totalOrder { + slices.SortStableFunc(types, CompareTypes) + } else if len(runStarts) > 0 { + types = c.mergeAscendingRuns(types, runStarts) + } unique := 1 for _, t := range types[1:] { if t != types[unique-1] { @@ -26541,10 +29278,128 @@ func (c *Checker) addTypesToUnion(sourceTypes []*Type, types []*Type) ([]*Type, unique++ } } - clear(types[unique:]) - types = types[:unique] + clear(types[unique:]) + types = types[:unique] + } + return types, includes +} + +func (c *Checker) mergeAscendingRuns(types []*Type, runStarts []int) []*Type { + n := len(types) + if len(runStarts) == 1 { + mid := runStarts[0] + if mid == 1 { + t := types[0] + i, _ := slices.BinarySearchFunc(types[1:], t, CompareTypes) + copy(types[:i], types[1:i+1]) + types[i] = t + return types + } + if mid == n-1 { + t := types[n-1] + i, _ := slices.BinarySearchFunc(types[:n-1], t, CompareTypes) + copy(types[i+1:], types[i:n-1]) + types[i] = t + return types + } + } + bounds := c.unionRunBounds[:0] + bounds = append(bounds, 0) + bounds = append(bounds, runStarts...) + bounds = append(bounds, n) + dst := c.unionMergeBuf + if cap(dst) < n { + dst = make([]*Type, 0, n) + } + src := types + swapped := false + next := c.unionRunNext[:0] + for len(bounds) > 2 { + runs := len(bounds) - 1 + next = next[:0] + next = append(next, 0) + dst = dst[:0] + for j := 0; j < runs; { + if j+1 < runs { + mergeSortedTypes(src[bounds[j]:bounds[j+1]], src[bounds[j+1]:bounds[j+2]], false /*dedup*/, func(t *Type) { + dst = append(dst, t) + }) + next = append(next, bounds[j+2]) + j += 2 + } else { + dst = append(dst, src[bounds[j]:bounds[j+1]]...) + next = append(next, bounds[j+1]) + j++ + } + } + src, dst = dst, src + swapped = !swapped + bounds, next = next, bounds + } + c.unionRunBounds = bounds + c.unionRunNext = next + if swapped { + copy(types, src) + clear(src) + c.unionMergeBuf = src[:0] + } else { + clear(dst) + c.unionMergeBuf = dst[:0] + } + return types +} + +func mergeSortedTypes(left []*Type, right []*Type, dedup bool, out func(*Type)) { + short, long := min(len(left), len(right)), max(len(left), len(right)) + if short > 0 && short*bits.Len(uint(long))*2 < long { + leftIsLong := len(left) >= len(right) + longRun, shortRun := right, left + if leftIsLong { + longRun, shortRun = left, right + } + for _, t := range shortRun { + i, _ := slices.BinarySearchFunc(longRun, t, CompareTypes) + for _, x := range longRun[:i] { + out(x) + } + longRun = longRun[i:] + out(t) + if len(longRun) > 0 && longRun[0] == t { + longRun = longRun[1:] + if !dedup { + out(t) + } + } + } + for _, x := range longRun { + out(x) + } + return + } + i, j := 0, 0 + for i < len(left) && j < len(right) { + l, r := left[i], right[j] + if l == r { + out(l) + if !dedup { + out(r) + } + i++ + j++ + } else if CompareTypes(l, r) < 0 { + out(l) + i++ + } else { + out(r) + j++ + } + } + for ; i < len(left); i++ { + out(left[i]) + } + for ; j < len(right); j++ { + out(right[j]) } - return types, includes } func (c *Checker) addNamedUnions(namedUnions []*Type, types []*Type) []*Type { @@ -26561,6 +29416,16 @@ func (c *Checker) addNamedUnions(namedUnions []*Type, types []*Type) []*Type { return namedUnions } +func (c *Checker) containsRegularLiteralType(types []*Type, i int, regular *Type) bool { + if i > 0 && types[i-1] == regular || i+1 < len(types) && types[i+1] == regular { + return true + } + if len(c.files) > 0 { + return false + } + return containsType(types, regular) +} + func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, reduceVoidUndefined bool) []*Type { i := len(types) for i > 0 { @@ -26572,7 +29437,7 @@ func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, flags&TypeFlagsBigIntLiteral != 0 && includes&TypeFlagsBigInt != 0 || flags&TypeFlagsUniqueESSymbol != 0 && includes&TypeFlagsESSymbol != 0 || reduceVoidUndefined && flags&TypeFlagsUndefined != 0 && includes&TypeFlagsVoid != 0 || - isFreshLiteralType(t) && containsType(types, t.AsLiteralType().regularType) + isFreshLiteralType(t) && c.containsRegularLiteralType(types, i, t.AsLiteralType().regularType) if remove { types = slices.Delete(types, i, i+1) } @@ -26581,16 +29446,36 @@ func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, } func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type { - templates := core.Filter(types, c.isPatternLiteralType) + var templates []*Type + for _, t := range types { + if c.isPatternLiteralType(t) { + templates = append(templates, t) + } + } if len(templates) != 0 { i := len(types) for i > 0 { i-- t := types[i] - if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool { - return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template) - }) { - types = slices.Delete(types, i, i+1) + if t.flags&TypeFlagsStringLiteral != 0 { + val := t.AsLiteralType().value.(string) + matched := false + for _, template := range templates { + if template.flags&TypeFlagsTemplateLiteral != 0 { + texts := template.AsTemplateLiteralType().texts + start, end := texts[0], texts[len(texts)-1] + if len(val) < len(start)+len(end) || !strings.HasPrefix(val, start) || !strings.HasSuffix(val, end) { + continue + } + } + if c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template) { + matched = true + break + } + } + if matched { + types = slices.Delete(types, i, i+1) + } } } } @@ -26670,11 +29555,13 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty // object types, and if none of those are present we can exclude primitive types from the subtype check. hasEmptyObject := hasObjectTypes && core.Some(types, func(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) + return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && c.isEmptyObjectType(t) }) length := len(types) i := length count := 0 + var keyTypes map[*Type]*Type + var lastKeyName string for i > 0 { i-- source := types[i] @@ -26691,8 +29578,8 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { return t })), c.strictSubtypeRelation) { types = slices.Delete(types, i, i+1) + continue } - continue } // Find the first property with a unit type, if any. When constituents have a property by the same name // but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype @@ -26700,12 +29587,25 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { var keyProperty *ast.Symbol var keyPropertyType *Type if source.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { - keyProperty = core.Find(c.getPropertiesOfType(source), func(p *ast.Symbol) bool { - return isUnitType(c.getTypeOfSymbol(p)) - }) - } - if keyProperty != nil { - keyPropertyType = c.getRegularTypeOfLiteralType(c.getTypeOfSymbol(keyProperty)) + props, lazySource := c.getPropertiesOfTypeLazily(source) + for _, p := range props { + if p.Flags()&ast.SymbolFlagsMethod != 0 && p.Flags()&(ast.SymbolFlagsProperty|ast.SymbolFlagsVariable|ast.SymbolFlagsGetAccessor|ast.SymbolFlagsEnumMember) == 0 { + continue + } + if lazySource != nil { + if lazySource.objectFlags&ObjectFlagsMapped == 0 { + if links := c.valueSymbolLinks.TryGet(p); links != nil && links.resolvedType != nil && !isUnitType(links.resolvedType) && !c.couldContainTypeVariables(links.resolvedType) { + continue + } + } + p = c.getPropertyOfType(lazySource, p.Name()) + } + if pt := c.getTypeOfSymbol(p); isUnitType(pt) { + keyProperty = p + keyPropertyType = c.getRegularTypeOfLiteralType(pt) + break + } + } } for _, target := range types { if source != target { @@ -26725,8 +29625,23 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { } count++ if keyProperty != nil && target.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { - t := c.getTypeOfPropertyOfType(target, keyProperty.Name()) - if t != nil && isUnitType(t) && c.getRegularTypeOfLiteralType(t) != keyPropertyType { + keyName := keyProperty.Name() + if keyName != lastKeyName { + lastKeyName = keyName + if keyTypes == nil { + keyTypes = make(map[*Type]*Type, len(types)) + } else { + clear(keyTypes) + } + } + t, ok := keyTypes[target] + if !ok { + if raw := c.getTypeOfPropertyOfType(target, keyName); raw != nil && isUnitType(raw) { + t = c.getRegularTypeOfLiteralType(raw) + } + keyTypes[target] = t + } + if t != nil && t != keyPropertyType { continue } } @@ -26755,7 +29670,40 @@ func (c *Checker) intersectTypes(type1 *Type, type2 *Type) *Type { case type2 == nil: return type1 } - return c.getIntersectionType([]*Type{type1, type2}) + pair := c.getTemporaryTypeArguments(2) + pair[0] = type1 + pair[1] = type2 + res := c.getIntersectionType(pair) + c.releaseTemporaryTypeArguments(pair) + return res +} + +func (c *Checker) everyTypeIsPrimitiveOrObjectOrEmpty(t *Type) bool { + if t.flags&TypeFlagsUnion != 0 { + for _, sub := range t.Types() { + if !c.isPrimitiveOrObjectOrEmptyType(sub) { + return false + } + } + return true + } + return c.isPrimitiveOrObjectOrEmptyType(t) +} + +func (c *Checker) someTypeIsStrictSubtypeOf(t *Type, target *Type) bool { + if t.flags&TypeFlagsUnion != 0 { + for _, sub := range t.Types() { + if c.isTypeStrictSubtypeOf(sub, target) { + return true + } + } + return false + } + return c.isTypeStrictSubtypeOf(t, target) +} + +func (c *Checker) someContainsMissingType(types []*Type) bool { + return slices.ContainsFunc(types, c.containsMissingType) } type IntersectionFlags uint32 @@ -26866,15 +29814,13 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, // We have an intersection T & P or P & T, where T is a type variable and P is a primitive type, the object type, or {}. constraint := c.getBaseConstraintOfType(typeVariable) // Check that T's constraint is similarly composed of primitive types, the object type, or {}. - if constraint != nil && everyType(constraint, c.isPrimitiveOrObjectOrEmptyType) { + if constraint != nil && c.everyTypeIsPrimitiveOrObjectOrEmpty(constraint) { // If T's constraint is a subtype of P, simply return T. For example, given `T extends "a" | "b"`, // the intersection `T & string` reduces to just T. if c.isTypeStrictSubtypeOf(constraint, primitiveType) { return typeVariable } - if !(constraint.flags&TypeFlagsUnion != 0 && someType(constraint, func(n *Type) bool { - return c.isTypeStrictSubtypeOf(n, primitiveType) - })) { + if !(constraint.flags&TypeFlagsUnion != 0 && c.someTypeIsStrictSubtypeOf(constraint, primitiveType)) { // No constituent of T's constraint is a subtype of P. If P is also not a subtype of T's constraint, // then the constraint and P are unrelated, and the intersection reduces to never. For example, given // `T extends "a" | "b"`, the intersection `T & number` reduces to never. @@ -26891,7 +29837,7 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, } } key := getIntersectionKey(typeSet, flags, alias) - result := c.intersectionTypes[key] + result := c.intersectionTypes.get(key) if result == nil { if includes&TypeFlagsUnion != 0 { var reduced bool @@ -26904,25 +29850,33 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, result = c.getIntersectionTypeEx(typeSet, flags, alias) case core.Every(typeSet, isUnionWithUndefined): containedUndefinedType := c.undefinedType - if core.Some(typeSet, c.containsMissingType) { + if c.someContainsMissingType(typeSet) { containedUndefinedType = c.missingType } c.filterTypes(typeSet, isNotUndefinedType) - result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), containedUndefinedType}, UnionReductionLiteral, alias, nil /*origin*/) + pair := c.getTemporaryTypeArguments(2) + pair[0] = c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/) + pair[1] = containedUndefinedType + result = c.getUnionTypeEx(pair, UnionReductionLiteral, alias, nil /*origin*/) + c.releaseTemporaryTypeArguments(pair) case core.Every(typeSet, isUnionWithNull): c.filterTypes(typeSet, isNotNullType) - result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), c.nullType}, UnionReductionLiteral, alias, nil /*origin*/) + pair := c.getTemporaryTypeArguments(2) + pair[0] = c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/) + pair[1] = c.nullType + result = c.getUnionTypeEx(pair, UnionReductionLiteral, alias, nil /*origin*/) + c.releaseTemporaryTypeArguments(pair) case len(typeSet) >= 3 && len(types) > 2: // When we have three or more constituents, more than two inputs (to head off infinite reexpansion), some of which are unions, we employ a "divide and conquer" strategy // where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can // dramatically reduce the overall work. middle := len(typeSet) / 2 - result = c.getIntersectionTypeEx([]*Type{ - c.getIntersectionTypeEx(typeSet[:middle], flags, nil /*alias*/), - c.getIntersectionTypeEx(typeSet[middle:], flags, nil /*alias*/), - }, - flags, alias) + pair := c.getTemporaryTypeArguments(2) + pair[0] = c.getIntersectionTypeEx(typeSet[:middle], flags, nil /*alias*/) + pair[1] = c.getIntersectionTypeEx(typeSet[middle:], flags, nil /*alias*/) + result = c.getIntersectionTypeEx(pair, flags, alias) + c.releaseTemporaryTypeArguments(pair) default: // We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of // the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type @@ -26930,7 +29884,7 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, if !c.checkCrossProductUnion(typeSet) { return c.errorType } - constituents := c.getCrossProductIntersections(typeSet, flags) + constituents, constituentsBuf := c.getCrossProductIntersections(typeSet, flags) // We attach a denormalized origin type when at least one constituent of the cross-product union is an // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions) and // the denormalized origin has fewer constituents than the union itself. @@ -26939,12 +29893,13 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, origin = c.newIntersectionType(ObjectFlagsNone, typeSet) } result = c.getUnionTypeEx(constituents, UnionReductionLiteral, alias, origin) + c.releaseTemporaryTypeArguments(constituentsBuf) } } else { result = c.newIntersectionType(objectFlags|c.getPropagatingFlagsOfTypes(types /*excludeKinds*/, TypeFlagsNullable), typeSet) result.alias = alias } - c.intersectionTypes[key] = result + c.intersectionTypes.set(c, key, result) } return result } @@ -27154,11 +30109,14 @@ func (c *Checker) unionContainsType(union *Type, t *Type, matchSymbol bool) bool return primitive != nil && containsType(types, primitive) } -func (c *Checker) getCrossProductIntersections(types []*Type, flags IntersectionFlags) []*Type { +func (c *Checker) getCrossProductIntersections(types []*Type, flags IntersectionFlags) ([]*Type, []*Type) { count := c.getCrossProductUnionSize(types) - var intersections []*Type + buf := c.getTemporaryTypeArguments(count) + intersections := buf[:0] + constituents := c.getTemporaryTypeArguments(len(types)) + copy(constituents, types) + defer c.releaseTemporaryTypeArguments(constituents) for i := range count { - constituents := slices.Clone(types) n := i for j := len(types) - 1; j >= 0; j-- { if types[j].flags&TypeFlagsUnion != 0 { @@ -27173,7 +30131,7 @@ func (c *Checker) getCrossProductIntersections(types []*Type, flags Intersection intersections = append(intersections, t) } } - return intersections + return intersections, buf } func getConstituentCount(t *Type) int { @@ -27202,7 +30160,7 @@ func (c *Checker) filterTypes(types []*Type, predicate func(*Type) bool) { func (c *Checker) IsEmptyAnonymousObjectType(t *Type) bool { return t.objectFlags&ObjectFlagsAnonymous != 0 && (t.objectFlags&ObjectFlagsMembersResolved != 0 && c.isEmptyResolvedType(t.AsStructuredType()) || - t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsTypeLiteral != 0 && len(c.getMembersOfSymbol(t.symbol)) == 0) + t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsTypeLiteral != 0 && c.getMembersOfSymbol(t.symbol).Len() == 0) } func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { @@ -27212,7 +30170,28 @@ func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { func (c *Checker) isEmptyObjectType(t *Type) bool { switch { case t.flags&TypeFlagsObject != 0: - return !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) + if c.isGenericMappedType(t) { + return false + } + if t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsAnonymous) == ObjectFlagsAnonymous && (t.symbol == nil || t.symbol.Flags()&ast.SymbolFlagsValueModule == 0) { + if d := t.AsObjectType(); d.target != nil { + targetResolved := c.resolveStructuredTypeMembers(d.target) + if targetMembers := targetResolved.getMembersIndex(); targetMembers == nil || targetMembers.fallback == nil { + return c.isEmptyResolvedType(targetResolved) + } + } + } + if lazy := c.getLazyMappedTable(t); lazy != nil && !lazy.resolving { + lazy.resolving = true + modProps, _ := c.getPropertiesOfTypeLazily(lazy.modifiersType) + lazy.resolving = false + for _, p := range modProps { + if !isLateBoundName(p.Name()) && !strings.HasPrefix(p.Name(), "\xFE#") && p.Flags()&ast.SymbolFlagsPrototype == 0 && getDeclarationModifierFlagsFromSymbol(p)&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { + return false + } + } + } + return c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) case t.flags&TypeFlagsNonPrimitive != 0: return true case t.flags&TypeFlagsUnion != 0: @@ -27285,22 +30264,51 @@ func everyContainedType(t *Type, f func(*Type) bool) bool { func (c *Checker) filterType(t *Type, f func(*Type) bool) *Type { if t.flags&TypeFlagsUnion != 0 { types := t.Types() - filtered := core.Filter(types, f) - if core.Same(types, filtered) { + firstRemoved := -1 + for i, u := range types { + if !f(u) { + firstRemoved = i + break + } + } + if firstRemoved < 0 { return t } + var filteredBuf [8]*Type + var tempBuf []*Type + var filtered []*Type + if len(types) <= len(filteredBuf) { + filtered = filteredBuf[:firstRemoved] + } else { + tempBuf = c.getTemporaryTypeArguments(len(types)) + defer c.releaseTemporaryTypeArguments(tempBuf) + filtered = tempBuf[:firstRemoved] + } + copy(filtered, types[:firstRemoved]) + for _, u := range types[firstRemoved+1:] { + if f(u) { + filtered = append(filtered, u) + } + } origin := t.AsUnionType().origin var newOrigin *Type if origin != nil && origin.flags&TypeFlagsUnion != 0 { - // If the origin type is a (denormalized) union type, filter its non-union constituents. If that ends - // up removing a smaller number of types than in the normalized constituent set (meaning some of the - // filtered types are within nested unions in the origin), then we can't construct a new origin type. - // Otherwise, if we have exactly one type left in the origin set, return that as the filtered type. - // Otherwise, construct a new filtered origin type. originTypes := origin.Types() - originFiltered := core.Filter(originTypes, func(u *Type) bool { - return u.flags&TypeFlagsUnion != 0 || f(u) - }) + var originBuf [8]*Type + var originTempBuf []*Type + var originFiltered []*Type + if len(originTypes) <= len(originBuf) { + originFiltered = originBuf[:0] + } else { + originTempBuf = c.getTemporaryTypeArguments(len(originTypes)) + defer c.releaseTemporaryTypeArguments(originTempBuf) + originFiltered = originTempBuf[:0] + } + for _, u := range originTypes { + if u.flags&TypeFlagsUnion != 0 || f(u) { + originFiltered = append(originFiltered, u) + } + } if len(originTypes)-len(originFiltered) == len(types)-len(filtered) { if len(originFiltered) == 1 { return originFiltered[0] @@ -27308,8 +30316,6 @@ func (c *Checker) filterType(t *Type, f func(*Type) bool) *Type { newOrigin = c.newUnionType(ObjectFlagsNone, originFiltered) } } - // filtering could remove intersections so `ContainsIntersections` might be forwarded "incorrectly" - // it is purely an optimization hint so there is no harm in accidentally forwarding it return c.getUnionTypeFromSortedList(filtered, t.AsUnionType().objectFlags&(ObjectFlagsPrimitiveUnion|ObjectFlagsContainsIntersections), nil /*alias*/, newOrigin) } if t.flags&TypeFlagsNever != 0 || f(t) { @@ -27329,10 +30335,16 @@ func (c *Checker) removeType(t *Type, targetType *Type) *Type { return c.filterType(t, func(t *Type) bool { return t != targetType }) } types := t.Types() - if i, ok := slices.BinarySearchFunc(types, targetType, CompareTypes); ok { - if len(types) == 2 { - return types[1-i] + if len(types) == 2 { + if types[0] == targetType { + return types[1] + } + if types[1] == targetType { + return types[0] } + return t + } + if i, ok := slices.BinarySearchFunc(types, targetType, CompareTypes); ok { // Remove the target type from the slice. set := c.getTemporaryTypeSet() defer c.releaseTemporaryTypeSet(set) @@ -27345,6 +30357,28 @@ func (c *Checker) removeType(t *Type, targetType *Type) *Type { } func containsType(types []*Type, t *Type) bool { + n := len(types) + if n == 0 { + return false + } + targetFlags := getSortOrderFlags(t) + if targetFlags < getSortOrderFlags(types[0]) || targetFlags > getSortOrderFlags(types[n-1]) { + return false + } + if t.flags < TypeFlagsStringLiteral { + for _, elem := range types { + if elem == t { + return true + } + if int(elem.flags) > targetFlags { + return false + } + } + return false + } + if n <= 24 { + return slices.Contains(types, t) + } _, ok := slices.BinarySearchFunc(types, t, CompareTypes) return ok } @@ -27417,7 +30451,7 @@ func (c *Checker) getIndexTypeEx(t *Type, indexFlags IndexFlags) *Type { switch { case c.isNoInferType(t): return c.getNoInferType(c.getIndexTypeEx(t.AsSubstitutionType().baseType, indexFlags)) - case c.shouldDeferIndexType(t, indexFlags): + case c.shouldDeferIndexType(t, indexFlags) || c.isBinaryKeyedTypeLiteral(t): return c.getIndexTypeForGenericType(t, indexFlags) case t.flags&TypeFlagsUnion != 0: return c.getIntersectionType(core.Map(t.Types(), func(t *Type) *Type { return c.getIndexTypeEx(t, indexFlags) })) @@ -27454,7 +30488,7 @@ func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, inclu if includeOrigin && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 || t.alias != nil { origin = c.newIndexType(t, IndexFlagsNone) } - props := c.getLayoutPropertiesOfType(t) + props, _ := c.getPropertiesOfTypeLazily(t) indexInfos := c.getIndexInfosOfType(t) types := make([]*Type, 0, len(props)+len(indexInfos)) for _, prop := range props { @@ -27476,7 +30510,7 @@ func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, inclu func (c *Checker) getLiteralTypeFromProperty(prop *ast.Symbol, include TypeFlags, includeNonPublic bool) *Type { if includeNonPublic || getDeclarationModifierFlagsFromSymbol(prop)&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { - t := c.valueSymbolLinks.Get(c.getLateBoundSymbol(prop)).getNameType() + t := c.valueSymbolLinks.TryGet(c.getLateBoundSymbol(prop)).getNameType() if t == nil { if prop.Name() == ast.InternalSymbolNameDefault { t = c.getStringLiteralType("default") @@ -27590,6 +30624,9 @@ func (c *Checker) getIndexTypeForGenericType(t *Type, indexFlags IndexFlags) *Ty return indexType } indexType := c.newIndexType(t, indexFlags&IndexFlagsStringsOnly) + if c.isBinaryKeyedTypeLiteral(t) { + indexType.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed + } c.cachedTypes[key] = indexType return indexType } @@ -27667,8 +30704,14 @@ func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *T if objectType == c.wildcardType || indexType == c.wildcardType { return c.wildcardType } + if accessNode == nil && alias == nil && accessFlags == AccessFlagsNone && !c.compilerOptions.NoUncheckedIndexedAccess.IsTrue() && isTupleType(objectType) { + if objectType.TargetTupleType().combinedFlags&(ElementFlagsOptional|ElementFlagsVariable) == 0 { + if fast := c.tryFastTupleIndexedAccess(objectType, indexType); fast != nil { + return fast + } + } + } objectType = c.getReducedType(objectType) - // If the object type has a string index signature and no other members we know that the result will // always be the type of that index signature and we can simplify accordingly. if c.isStringIndexSignatureOnlyType(objectType) && indexType.flags&TypeFlagsNullable == 0 && c.isTypeAssignableToKind(indexType, TypeFlagsString|TypeFlagsNumber) { indexType = c.stringType @@ -27691,11 +30734,11 @@ func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *T // Defer the operation by creating an indexed access type. persistentAccessFlags := accessFlags & AccessFlagsPersistent key := getIndexedAccessKey(objectType, indexType, accessFlags, alias) - t := c.indexedAccessTypes[key] + t := c.indexedAccessTypes.get(key) if t == nil { t = c.newIndexedAccessType(objectType, indexType, persistentAccessFlags) t.alias = alias - c.indexedAccessTypes[key] = t + c.indexedAccessTypes.set(c, key, t) } return t } @@ -27704,29 +30747,65 @@ func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *T // skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'. apparentObjectType := c.getReducedApparentType(objectType) if indexType.flags&TypeFlagsUnion != 0 && indexType.flags&TypeFlagsBoolean == 0 { - var propTypes []*Type - wasMissingProp := false - for _, t := range indexType.Types() { - propType := c.getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, accessNode, accessFlags|core.IfElse(wasMissingProp, AccessFlagsSuppressNoImplicitAnyError, 0)) - if propType != nil { - propTypes = append(propTypes, propType) - } else if accessNode == nil { - // If there's no error node, we can immediately stop, since error reporting is off - return nil - } else { - // Otherwise we set a flag and return at the end of the loop so we still mark all errors - wasMissingProp = true + if accessNode == nil && alias == nil { + memoKey := indexedAccessUnionKey{objectId: objectType.id, indexId: indexType.id, accessFlags: accessFlags} + if cached, ok := c.indexedAccessUnionMemo[memoKey]; ok { + return cached + } + typeCount := c.TypeCount + instCount := c.instantiationCount + totalInstCount := c.TotalInstantiationCount + impure := c.unionImpureCount + diagCount := c.diagnosticCount + result := c.getIndexedAccessTypeByUnionWorker(objectType, apparentObjectType, indexType, accessFlags, nil, nil) + created := c.TypeCount != typeCount && !(c.TypeCount == typeCount+1 && result != nil && result.id == TypeId(c.TypeCount)) + instantiated := c.instantiationCount != instCount || c.TotalInstantiationCount != totalInstCount + wasImpure := c.unionImpureCount != impure + reported := c.diagnosticCount != diagCount + if !created && !instantiated && !wasImpure && !reported && result != c.errorType { + if c.indexedAccessUnionMemo == nil { + c.indexedAccessUnionMemo = make(map[indexedAccessUnionKey]*Type) + } + c.indexedAccessUnionMemo[memoKey] = result } + return result } - if wasMissingProp { + return c.getIndexedAccessTypeByUnionWorker(objectType, apparentObjectType, indexType, accessFlags, accessNode, alias) + } + return c.getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags|AccessFlagsCacheSymbol|AccessFlagsReportDeprecated) +} + +type indexedAccessUnionKey struct { + objectId TypeId + indexId TypeId + accessFlags AccessFlags +} + +func (c *Checker) getIndexedAccessTypeByUnionWorker(objectType *Type, apparentObjectType *Type, indexType *Type, accessFlags AccessFlags, accessNode *ast.Node, alias *TypeAlias) *Type { + propTypes := c.getTemporaryTypeArguments(0) + defer func() { + c.releaseTemporaryTypeArguments(propTypes) + }() + wasMissingProp := false + for _, t := range indexType.Types() { + propType := c.getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, accessNode, accessFlags|core.IfElse(wasMissingProp, AccessFlagsSuppressNoImplicitAnyError, 0)) + if propType != nil { + propTypes = append(propTypes, propType) + } else if accessNode == nil { + // If there's no error node, we can immediately stop, since error reporting is off return nil + } else { + // Otherwise we set a flag and return at the end of the loop so we still mark all errors + wasMissingProp = true } - if accessFlags&AccessFlagsWriting != 0 { - return c.getIntersectionTypeEx(propTypes, IntersectionFlagsNone, alias) - } - return c.getUnionTypeEx(propTypes, UnionReductionLiteral, alias, nil) } - return c.getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags|AccessFlagsCacheSymbol|AccessFlagsReportDeprecated) + if wasMissingProp { + return nil + } + if accessFlags&AccessFlagsWriting != 0 { + return c.getIntersectionTypeEx(propTypes, IntersectionFlagsNone, alias) + } + return c.getUnionTypeEx(propTypes, UnionReductionLiteral, alias, nil) } func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectType *Type, indexType *Type, fullIndexType *Type, accessNode *ast.Node, accessFlags AccessFlags) *Type { @@ -27867,13 +30946,13 @@ func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectTy c.addDiagnostic(createDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))) return c.undefinedType } else if indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0 { - types := core.Map(objectType.AsStructuredType().getProperties(), func(prop *ast.Symbol) *Type { + types := core.Map(objectType.AsStructuredType().getPropertiesSlice(), func(prop *ast.Symbol) *Type { return c.getTypeOfSymbol(prop) }) return c.getUnionType(append(types, c.undefinedType)) } } - if objectType.symbol == c.globalThisSymbol && hasPropName && c.globalThisSymbol.Exports()[propName] != nil && c.globalThisSymbol.Exports()[propName].Flags()&ast.SymbolFlagsBlockScoped != 0 { + if objectType.symbol == c.globalThisSymbol && hasPropName && c.globalThisSymbol.Exports().Get(propName) != nil && c.globalThisSymbol.Exports().Get(propName).Flags()&ast.SymbolFlagsBlockScoped != 0 { c.error(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, propName, c.TypeToString(objectType)) } else if c.noImplicitAny && accessFlags&AccessFlagsSuppressNoImplicitAnyError == 0 { if hasPropName && c.typeHasStaticProperty(propName, objectType) { @@ -28094,12 +31173,12 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || + return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && !c.hasPropertiesOfStructuredType(t) && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) } func (c *Checker) shouldDeferIndexedAccessType(objectType *Type, indexType *Type, accessNode *ast.Node) bool { - if c.isGenericIndexType(indexType) { + if c.isGenericIndexType(indexType) || indexType.flags&TypeFlagsIndex != 0 { return true } if accessNode != nil && !ast.IsIndexedAccessTypeNode(accessNode) { @@ -28252,6 +31331,9 @@ func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type { } return nil case t.flags&TypeFlagsIndex != 0: + if c.isBinaryKeyedTypeLiteral(t.AsIndexType().target) { + return t + } if c.isGenericMappedType(t.AsIndexType().target) { mappedType := t.AsIndexType().target if c.getNameTypeFromMappedType(mappedType) != nil && !c.isMappedTypeWithKeyofConstraintDeclaration(mappedType) { @@ -28338,16 +31420,48 @@ func (c *Checker) maybeTypeOfKind(t *Type, kind TypeFlags) bool { if t.flags&kind != 0 { return true } - if t.flags&TypeFlagsUnionOrIntersection != 0 { - for _, t := range t.Types() { - if c.maybeTypeOfKind(t, kind) { - return true - } + if t.flags&TypeFlagsUnionOrIntersection == 0 { + return false + } + if kind&^TypeFlagsNotPrimitiveUnion == 0 && t.objectFlags&ObjectFlagsPrimitiveUnion != 0 { + return false + } + types := t.Types() + if len(types) >= 8 { + return c.constituentFlagsOfUnionOrIntersection(t)&kind != 0 + } + for _, ct := range types { + if ct.flags&kind != 0 || ct.flags&TypeFlagsUnionOrIntersection != 0 && c.maybeTypeOfKind(ct, kind) { + return true } } return false } +func (c *Checker) constituentFlagsOfUnionOrIntersection(t *Type) TypeFlags { + types := t.Types() + large := len(types) >= 8 + if large && c.unionConstituentFlags != nil { + if flags, ok := c.unionConstituentFlags[t]; ok { + return flags + } + } + var flags TypeFlags + for _, ct := range types { + flags |= ct.flags + if ct.flags&TypeFlagsUnionOrIntersection != 0 { + flags |= c.constituentFlagsOfUnionOrIntersection(ct) + } + } + if large { + if c.unionConstituentFlags == nil { + c.unionConstituentFlags = make(map[*Type]TypeFlags) + } + c.unionConstituentFlags[t] = flags + } + return flags +} + func (c *Checker) maybeTypeOfKindConsideringBaseConstraint(t *Type, kind TypeFlags) bool { if c.maybeTypeOfKind(t, kind) { return true @@ -28455,9 +31569,13 @@ func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOn } target := prop if prop.CheckFlags()&ast.CheckFlagsInstantiated != 0 { - target = c.valueSymbolLinks.Get(prop).target + if links := c.valueSymbolLinks.TryGet(prop); links != nil { + target = links.target + } else { + target = nil + } } - c.symbolReferenceLinks.Get(target).referenceKinds |= ast.SymbolFlagsAll + target.AddReference(ast.SymbolFlagsAll) } func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature, restType *TypeReference, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { @@ -28590,7 +31708,7 @@ func (c *Checker) containsUndefinedType(t *Type) bool { } func (c *Checker) typeHasCallOrConstructSignatures(t *Type) bool { - return t.flags&TypeFlagsStructuredType != 0 && len(c.resolveStructuredTypeMembers(t).getSignatures()) != 0 + return t.flags&TypeFlagsStructuredType != 0 && (len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) != 0 || len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) != 0) } func (c *Checker) getNormalizedType(t *Type, writing bool) *Type { @@ -28755,22 +31873,43 @@ func (c *Checker) getSimplifiedConditionalType(t *Type, writing bool) *Type { } func (c *Checker) getSimplifiedConditionalTypeWorker(t *Type, writing bool) *Type { - checkType := t.AsConditionalType().checkType - extendsType := t.AsConditionalType().extendsType - trueType := c.getTrueTypeFromConditionalType(t) - falseType := c.getFalseTypeFromConditionalType(t) + d := t.AsConditionalType() + checkType := d.checkType + actualCheck := c.getActualTypeVariable(checkType) + rawTrue := c.getTypeFromTypeNode(d.root.node.TrueType) + if rawTrue.flags&TypeFlagsObject != 0 && rawTrue.objectFlags&(ObjectFlagsAnonymous|ObjectFlagsClassOrInterface|ObjectFlagsMapped) != 0 && rawTrue.objectFlags&ObjectFlagsReverseMapped == 0 && actualCheck.flags&TypeFlagsObject == 0 { + return t + } + extendsType := d.extendsType + var falseType *Type + if rawFalse := c.getTypeFromTypeNode(d.root.node.FalseType); rawFalse.flags&TypeFlagsNever != 0 { + falseType = c.neverType + } else { + falseType = c.getFalseTypeFromConditionalType(t) + } // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - if falseType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(trueType) == c.getActualTypeVariable(checkType) { - if checkType.flags&TypeFlagsAny != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { - return c.getSimplifiedType(trueType, writing) - } else if c.isIntersectionEmpty(checkType, extendsType) { - return c.neverType + if falseType.flags&TypeFlagsNever != 0 { + trueType := c.getTrueTypeFromConditionalType(t) + if c.getActualTypeVariable(trueType) == actualCheck { + if checkType.flags&TypeFlagsAny != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { + return c.getSimplifiedType(trueType, writing) + } else if c.isIntersectionEmpty(checkType, extendsType) { + return c.neverType + } + } + } else if c.getActualTypeVariable(falseType) == actualCheck { + var trueType *Type + if rawTrue.flags&TypeFlagsNever != 0 { + trueType = c.neverType + } else { + trueType = c.getTrueTypeFromConditionalType(t) } - } else if trueType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(falseType) == c.getActualTypeVariable(checkType) { - if checkType.flags&TypeFlagsAny == 0 && c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { - return c.neverType - } else if checkType.flags&TypeFlagsAny != 0 || c.isIntersectionEmpty(checkType, extendsType) { - return c.getSimplifiedType(falseType, writing) + if trueType.flags&TypeFlagsNever != 0 { + if checkType.flags&TypeFlagsAny == 0 && c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { + return c.neverType + } else if checkType.flags&TypeFlagsAny != 0 || c.isIntersectionEmpty(checkType, extendsType) { + return c.getSimplifiedType(falseType, writing) + } } } return t @@ -28809,15 +31948,20 @@ func (c *Checker) getNormalizedUnionOrIntersectionType(t *Type, writing bool) *T } func (c *Checker) shouldNormalizeIntersection(t *Type) bool { + if t.cacheFlags&typeCacheShouldNormalizeComputed != 0 { + return t.cacheFlags&typeCacheShouldNormalize != 0 + } hasInstantiable := false hasNullableOrEmpty := false - for _, t := range t.Types() { - hasInstantiable = hasInstantiable || t.flags&TypeFlagsInstantiable != 0 - hasNullableOrEmpty = hasNullableOrEmpty || t.flags&TypeFlagsNullable != 0 || c.IsEmptyAnonymousObjectType(t) + for _, u := range t.Types() { + hasInstantiable = hasInstantiable || u.flags&TypeFlagsInstantiable != 0 + hasNullableOrEmpty = hasNullableOrEmpty || u.flags&TypeFlagsNullable != 0 || c.IsEmptyAnonymousObjectType(u) if hasInstantiable && hasNullableOrEmpty { + t.cacheFlags |= typeCacheShouldNormalizeComputed | typeCacheShouldNormalize return true } } + t.cacheFlags |= typeCacheShouldNormalizeComputed return false } @@ -28836,15 +31980,22 @@ func (c *Checker) getNormalizedTupleType(t *Type, writing bool) *Type { } func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference == 0 || t.Target().objectFlags&ObjectFlagsClassOrInterface == 0 { + if t.objectFlags&ObjectFlagsReference == 0 || t.objectFlags&(ObjectFlagsIdenticalBaseTypeCalculated|ObjectFlagsIdenticalBaseTypeExists) == ObjectFlagsIdenticalBaseTypeCalculated { return nil } + return c.getSingleBaseForNonAugmentingSubtypeSlow(t) +} + +func (c *Checker) getSingleBaseForNonAugmentingSubtypeSlow(t *Type) *Type { key := CachedTypeKey{kind: CachedTypeKindEquivalentBaseType, typeId: t.id} - if t.objectFlags&ObjectFlagsIdenticalBaseTypeCalculated != 0 { + if t.objectFlags&ObjectFlagsIdenticalBaseTypeExists != 0 { return c.cachedTypes[key] } t.objectFlags |= ObjectFlagsIdenticalBaseTypeCalculated target := t.Target() + if target.objectFlags&ObjectFlagsClassOrInterface == 0 { + return nil + } if target.objectFlags&ObjectFlagsClass != 0 { baseTypeNode := getBaseTypeNodeOfClass(target) // A base type expression may circularly reference the class itself (e.g. as an argument to function call), so we only @@ -28857,7 +32008,7 @@ func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { if len(bases) != 1 { return nil } - if len(c.getMembersOfSymbol(t.symbol)) != 0 { + if c.getMembersOfSymbol(t.symbol).Len() != 0 { // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison return nil } @@ -28871,7 +32022,10 @@ func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { if len(c.getTypeArguments(t)) > len(typeParameters) { instantiatedBase = c.getTypeWithThisArgument(instantiatedBase, core.LastOrNil(c.getTypeArguments(t)), false) } - c.cachedTypes[key] = instantiatedBase + if instantiatedBase != nil { + t.objectFlags |= ObjectFlagsIdenticalBaseTypeExists + c.cachedTypes[key] = instantiatedBase + } return instantiatedBase } @@ -28925,14 +32079,14 @@ func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type { } func (c *Checker) transformTypeOfMembers(t *Type, f func(propertyType *Type) *Type) ast.SymbolTable { - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for _, property := range c.getPropertiesOfObjectType(t) { original := c.getTypeOfSymbol(property) updated := f(original) if updated != original { property = c.createSymbolWithType(property, updated) } - members[property.Name()] = property + members.Set(property.Name(), property) } return members } @@ -29900,6 +33054,42 @@ const ( maxTemplateLiteralTypeSpans = 100_000 ) +type templateLiteralScratch struct { + buf []byte + newTypes []*Type + newTexts []string +} + +func (c *Checker) acquireTemplateScratch() *templateLiteralScratch { + if n := len(c.templateScratchPool); n > 0 { + s := c.templateScratchPool[n-1] + c.templateScratchPool = c.templateScratchPool[:n-1] + return s + } + return &templateLiteralScratch{} +} + +func (c *Checker) releaseTemplateScratch(s *templateLiteralScratch, buf []byte, newTypes []*Type, newTexts []string) { + if cap(buf) <= 65536 { + s.buf = buf[:0] + } else { + s.buf = nil + } + clear(newTypes) + if cap(newTypes) <= 4096 { + s.newTypes = newTypes[:0] + } else { + s.newTypes = nil + } + clear(newTexts) + if cap(newTexts) <= 4096 { + s.newTexts = newTexts[:0] + } else { + s.newTexts = nil + } + c.templateScratchPool = append(c.templateScratchPool, s) +} + func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { unionIndex := core.FindIndex(types, func(t *Type) bool { return t.flags&(TypeFlagsNever|TypeFlagsUnion) != 0 @@ -29915,10 +33105,43 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { if slices.Contains(types, c.wildcardType) { return c.wildcardType } - var newTypes []*Type - var newTexts []string - var sb strings.Builder - sb.WriteString(texts[0]) + scratch := c.acquireTemplateScratch() + buf := scratch.buf[:0] + newTypes := scratch.newTypes[:0] + newTexts := scratch.newTexts[:0] + curStr := texts[0] + usingBuf := false + appendStr := func(s string) { + if len(s) == 0 { + return + } + if !usingBuf { + if len(curStr) == 0 { + curStr = s + return + } + buf = append(buf[:0], curStr...) + buf = append(buf, s...) + usingBuf = true + } else { + buf = append(buf, s...) + } + } + curLen := func() int { + if usingBuf { + return len(buf) + } + return len(curStr) + } + flushStr := func() string { + if !usingBuf { + return stringutil.CombineSurrogatePairs(curStr) + } + if slices.Contains(buf, 0xed) { + return stringutil.CombineSurrogatePairs(string(buf)) + } + return string(buf) + } textLength := 0 // combined length of the segments already moved into newTexts tooLarge := false var addSpans func([]string, []*Type) bool @@ -29926,24 +33149,24 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { for i, t := range types { switch { case t.flags&(TypeFlagsLiteral|TypeFlagsNull|TypeFlagsUndefined) != 0: - sb.WriteString(c.getTemplateStringForType(t)) - sb.WriteString(texts[i+1]) + appendStr(c.getTemplateStringForType(t)) + appendStr(texts[i+1]) case t.flags&TypeFlagsTemplateLiteral != 0: - sb.WriteString(t.AsTemplateLiteralType().texts[0]) + appendStr(t.AsTemplateLiteralType().texts[0]) if !addSpans(t.AsTemplateLiteralType().texts, t.AsTemplateLiteralType().types) { return false } - sb.WriteString(texts[i+1]) + appendStr(texts[i+1]) case c.isGenericIndexType(t) || c.isPatternLiteralPlaceholderType(t): newTypes = append(newTypes, t) - newTexts = append(newTexts, stringutil.CombineSurrogatePairs(sb.String())) - textLength += sb.Len() - sb.Reset() - sb.WriteString(texts[i+1]) + newTexts = append(newTexts, flushStr()) + textLength += curLen() + curStr = texts[i+1] + usingBuf = false default: return false } - if textLength+sb.Len() > maxTemplateLiteralTypeLength || len(newTypes) > maxTemplateLiteralTypeSpans { + if textLength+curLen() > maxTemplateLiteralTypeLength || len(newTypes) > maxTemplateLiteralTypeSpans { tooLarge = true return false } @@ -29951,6 +33174,7 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { return true } if !addSpans(texts, types) { + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) if tooLarge { c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) return c.errorType @@ -29958,24 +33182,45 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { return c.stringType } if len(newTypes) == 0 { - return c.getStringLiteralType(stringutil.CombineSurrogatePairs(sb.String())) + if !usingBuf { + s := stringutil.CombineSurrogatePairs(curStr) + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) + return c.getStringLiteralType(s) + } + hasSurrogate := slices.Contains(buf, 0xed) + if !hasSurrogate { + if t := c.stringLiteralTypes[string(buf)]; t != nil { + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) + return t + } + s := string(buf) + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) + return c.getStringLiteralType(s) + } + s := stringutil.CombineSurrogatePairs(string(buf)) + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) + return c.getStringLiteralType(s) } - newTexts = append(newTexts, stringutil.CombineSurrogatePairs(sb.String())) + newTexts = append(newTexts, flushStr()) if core.Every(newTexts, func(t string) bool { return t == "" }) { if core.Every(newTypes, func(t *Type) bool { return t.flags&TypeFlagsString != 0 }) { + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) return c.stringType } - // Normalize `${Mapping}` into Mapping + // Normalize `${Mapping}` into `Mapping` if len(newTypes) == 1 && c.isPatternLiteralType(newTypes[0]) { - return newTypes[0] + res := newTypes[0] + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) + return res } } - key := getTemplateTypeKey(newTexts, newTypes) - t := c.templateLiteralTypes[key] + key := c.getTemplateTypeKey(newTexts, newTypes) + t := c.templateLiteralTypes.get(key) if t == nil { - t = c.newTemplateLiteralType(newTexts, newTypes) - c.templateLiteralTypes[key] = t + t = c.newTemplateLiteralType(slices.Clone(newTexts), slices.Clone(newTypes)) + c.templateLiteralTypes.setSmall(c, key, t) } + c.releaseTemplateScratch(scratch, buf, newTypes, newTexts) return t } @@ -30553,7 +33798,9 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. var signature *Signature - if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature { + if links := c.signatureLinks.TryGet(callTarget); links != nil && links.resolvedSignature == c.resolvingSignature { + c.resolutionCycleCount++ + c.flowMemo.taint(0) signature = c.resolvingSignature } else { signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal) @@ -30563,7 +33810,7 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI } restIndex := len(signature.parameters) - 1 if signatureHasRestParameter(signature) && argIndex >= restIndex { - return c.getIndexedAccessTypeEx(c.getTypeOfSymbol(signature.parameters[restIndex]), c.getNumberLiteralType(jsnum.Number(argIndex-restIndex)), AccessFlagsContextual, nil, nil) + return c.getIndexedAccessTypeEx(c.getTypeOfSignatureParameterSymbol(signature, restIndex), c.getNumberLiteralType(jsnum.Number(argIndex-restIndex)), AccessFlagsContextual, nil, nil) } return c.getTypeAtPosition(signature, argIndex) } @@ -30699,7 +33946,7 @@ func (c *Checker) getContextualTypeForObjectLiteralElement(element *ast.Node, co // in the type. It will just be "__computed", which does not appear in any // SymbolTable. symbol := c.getSymbolOfDeclaration(element) - return c.getTypeOfPropertyOfContextualTypeEx(t, symbol.Name(), c.valueSymbolLinks.Get(symbol).getNameType()) + return c.getTypeOfPropertyOfContextualTypeEx(t, symbol.Name(), c.valueSymbolLinks.TryGet(symbol).getNameType()) } if ast.HasDynamicName(element) { name := ast.GetNameOfDeclaration(element) @@ -30714,14 +33961,36 @@ func (c *Checker) getContextualTypeForObjectLiteralElement(element *ast.Node, co } } if element.Name() != nil { - nameType := c.getLiteralTypeFromPropertyName(element.Name()) - // We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction. + // We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction + if t.flags&TypeFlagsNever != 0 { + return t + } + if t.flags&TypeFlagsUnion == 0 { + indexInfos := c.getIndexInfosOfStructuredType(t) + if len(indexInfos) == 0 { + return nil + } + nameType := c.getLiteralTypeFromPropertyName(element.Name()) + indexInfo := c.findApplicableIndexInfo(indexInfos, nameType) + if indexInfo != nil { + return indexInfo.valueType + } + return nil + } + var nameType *Type return c.mapTypeEx(t, func(t *Type) *Type { - indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType) - if indexInfo == nil { + indexInfos := c.getIndexInfosOfStructuredType(t) + if len(indexInfos) == 0 { return nil } - return indexInfo.valueType + if nameType == nil { + nameType = c.getLiteralTypeFromPropertyName(element.Name()) + } + indexInfo := c.findApplicableIndexInfo(indexInfos, nameType) + if indexInfo != nil { + return indexInfo.valueType + } + return nil }, true /*noReductions*/) } } @@ -30932,15 +34201,15 @@ func (c *Checker) getDecoratorCallSignature(decorator *ast.Node) *Signature { func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signature { node := decorator.Parent links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature + if links.effectsOrDecoratorSignature == nil { + links.effectsOrDecoratorSignature = c.anySignature switch node.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: // For a class decorator, the `target` is the type of the class (e.g. the // "static" or "constructor" side of the class). targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) targetParam := c.newParameter("target", targetType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType})) + links.effectsOrDecoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType})) case ast.KindParameter: if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) { break @@ -30965,7 +34234,7 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur targetParam := c.newParameter("target", targetType) keyParam := c.newParameter("propertyKey", keyType) indexParam := c.newParameter("parameterIndex", indexType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType) + links.effectsOrDecoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType) case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration: if !ast.IsClassLike(node.Parent) { break @@ -30984,16 +34253,16 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur if hasPropDesc { descriptorType := c.newTypedPropertyDescriptorType(c.getTypeOfNode(node)) descriptorParam := c.newParameter("descriptor", descriptorType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType})) + links.effectsOrDecoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType})) } else { - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType})) + links.effectsOrDecoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType})) } } } - if links.decoratorSignature == c.anySignature { + if links.effectsOrDecoratorSignature == c.anySignature { return nil } - return links.decoratorSignature + return links.effectsOrDecoratorSignature } func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { @@ -31079,15 +34348,15 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { // `undefined` then the "output type" is the "input type"). node := decorator.Parent links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature + if links.effectsOrDecoratorSignature == nil { + links.effectsOrDecoratorSignature = c.anySignature switch node.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: // Class decorators have a `context` of `ClassDecoratorContext`, where the `Class` type // argument will be the "final type" of the class after all decorators are applied. targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) contextType := c.newClassDecoratorContextType(targetType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) + links.effectsOrDecoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: if !ast.IsClassLike(node.Parent) { break @@ -31128,7 +34397,7 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { targetType = valueType } contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) + links.effectsOrDecoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) case ast.KindPropertyDeclaration: if !ast.IsClassLike(node.Parent) { break @@ -31165,13 +34434,13 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { returnType = c.newClassFieldDecoratorInitializerMutatorType(thisType, valueType) } contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, returnType) + links.effectsOrDecoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, returnType) } } - if links.decoratorSignature == c.anySignature { + if links.effectsOrDecoratorSignature == c.anySignature { return nil } - return links.decoratorSignature + return links.effectsOrDecoratorSignature } func (c *Checker) newClassDecoratorContextType(classType *Type) *Type { @@ -31210,10 +34479,10 @@ func (c *Checker) getClassMemberDecoratorContextOverrideType(nameType *Type, isP if overrideType := c.cachedTypes[key]; overrideType != nil { return overrideType } - members := make(ast.SymbolTable) - members["name"] = c.newProperty("name", nameType) - members["private"] = c.newProperty("private", core.IfElse(isPrivate, c.trueType, c.falseType)) - members["static"] = c.newProperty("static", core.IfElse(isStatic, c.trueType, c.falseType)) + members := ast.NewSymbolTable() + members.Set("name", c.newProperty("name", nameType)) + members.Set("private", c.newProperty("private", core.IfElse(isPrivate, c.trueType, c.falseType))) + members.Set("static", c.newProperty("static", core.IfElse(isStatic, c.trueType, c.falseType))) overrideType := c.newAnonymousType(nil, members, nil, nil, nil) c.cachedTypes[key] = overrideType return overrideType @@ -31323,55 +34592,65 @@ func (c *Checker) getTypeOfPropertyOfContextualType(t *Type, name string) *Type } func (c *Checker) getTypeOfPropertyOfContextualTypeEx(t *Type, name string, nameType *Type) *Type { + if t.flags&TypeFlagsNever != 0 { + return t + } + if t.flags&TypeFlagsUnion == 0 { + return c.getTypeOfPropertyOfContextualTypeWorker(t, name, nameType) + } return c.mapTypeEx(t, func(t *Type) *Type { - if t.flags&TypeFlagsIntersection != 0 { - var types []*Type - var indexInfoCandidates []*Type - ignoreIndexInfos := false - for _, constituentType := range t.Types() { - if constituentType.flags&TypeFlagsObject == 0 { - continue - } - if c.isGenericMappedType(constituentType) && c.getMappedTypeNameTypeKind(constituentType) != MappedTypeNameTypeKindRemapping { - substitutedType := c.getIndexedMappedTypeSubstitutedTypeOfContextualType(constituentType, name, nameType) - types = c.appendContextualPropertyTypeConstituent(types, substitutedType) - continue - } - propertyType := c.getTypeOfConcretePropertyOfContextualType(constituentType, name) - if propertyType == nil { - if !ignoreIndexInfos { - indexInfoCandidates = append(indexInfoCandidates, constituentType) - } - continue - } - ignoreIndexInfos = true - indexInfoCandidates = nil - types = c.appendContextualPropertyTypeConstituent(types, propertyType) - } - for _, candidate := range indexInfoCandidates { - indexInfoType := c.getTypeFromIndexInfosOfContextualType(candidate, name, nameType) - types = c.appendContextualPropertyTypeConstituent(types, indexInfoType) + return c.getTypeOfPropertyOfContextualTypeWorker(t, name, nameType) + }, true /*noReductions*/) +} + +func (c *Checker) getTypeOfPropertyOfContextualTypeWorker(t *Type, name string, nameType *Type) *Type { + if t.flags&TypeFlagsIntersection != 0 { + var types []*Type + var indexInfoCandidates []*Type + ignoreIndexInfos := false + for _, constituentType := range t.Types() { + if constituentType.flags&TypeFlagsObject == 0 { + continue } - if len(types) == 0 { - return nil + if c.isGenericMappedType(constituentType) && c.getMappedTypeNameTypeKind(constituentType) != MappedTypeNameTypeKindRemapping { + substitutedType := c.getIndexedMappedTypeSubstitutedTypeOfContextualType(constituentType, name, nameType) + types = c.appendContextualPropertyTypeConstituent(types, substitutedType) + continue } - if len(types) == 1 { - return types[0] + propertyType := c.getTypeOfConcretePropertyOfContextualType(constituentType, name) + if propertyType == nil { + if !ignoreIndexInfos { + indexInfoCandidates = append(indexInfoCandidates, constituentType) + } + continue } - return c.getIntersectionType(types) + ignoreIndexInfos = true + indexInfoCandidates = nil + types = c.appendContextualPropertyTypeConstituent(types, propertyType) } - if t.flags&TypeFlagsObject == 0 { - return nil + for _, candidate := range indexInfoCandidates { + indexInfoType := c.getTypeFromIndexInfosOfContextualType(candidate, name, nameType) + types = c.appendContextualPropertyTypeConstituent(types, indexInfoType) } - if c.isGenericMappedType(t) && c.getMappedTypeNameTypeKind(t) != MappedTypeNameTypeKindRemapping { - return c.getIndexedMappedTypeSubstitutedTypeOfContextualType(t, name, nameType) + if len(types) == 0 { + return nil } - result := c.getTypeOfConcretePropertyOfContextualType(t, name) - if result != nil { - return result + if len(types) == 1 { + return types[0] } - return c.getTypeFromIndexInfosOfContextualType(t, name, nameType) - }, true /*noReductions*/) + return c.getIntersectionType(types) + } + if t.flags&TypeFlagsObject == 0 { + return nil + } + if c.isGenericMappedType(t) && c.getMappedTypeNameTypeKind(t) != MappedTypeNameTypeKindRemapping { + return c.getIndexedMappedTypeSubstitutedTypeOfContextualType(t, name, nameType) + } + result := c.getTypeOfConcretePropertyOfContextualType(t, name) + if result != nil { + return result + } + return c.getTypeFromIndexInfosOfContextualType(t, name, nameType) } func (c *Checker) getIndexedMappedTypeSubstitutedTypeOfContextualType(t *Type, name string, nameType *Type) *Type { @@ -31420,10 +34699,14 @@ func (c *Checker) getTypeFromIndexInfosOfContextualType(t *Type, name string, na return restType } } + indexInfos := c.getIndexInfosOfStructuredType(t) + if len(indexInfos) == 0 { + return nil + } if nameType == nil { nameType = c.getStringLiteralType(name) } - indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType) + indexInfo := c.findApplicableIndexInfo(indexInfos, nameType) if indexInfo == nil { return nil } @@ -31543,7 +34826,7 @@ func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, cont return false }) discriminantMembers := core.Filter(c.getLayoutPropertiesOfType(contextualType), func(s *ast.Symbol) bool { - return s.Flags()&ast.SymbolFlagsOptional != 0 && node.Symbol().Members()[s.Name()] == nil && c.isDiscriminantProperty(contextualType, s.Name()) + return s.Flags()&ast.SymbolFlagsOptional != 0 && node.Symbol().Members().Get(s.Name()) == nil && c.isDiscriminantProperty(contextualType, s.Name()) }) discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} discriminated = c.discriminateTypeByDiscriminableItems(contextualType, discriminator) @@ -31874,6 +35157,12 @@ func (c *Checker) getTypeFactsWorker(t *Type, callerOnlyNeeds TypeFacts) TypeFac case flags&TypeFlagsNever != 0: return TypeFactsNone case flags&TypeFlagsUnion != 0: + if t.cacheFlags&typeCacheNonNullableUnion != 0 { + possibleFacts := core.IfElse(c.strictNullChecks, TypeFactsEmptyObjectStrictFacts, TypeFactsEmptyObjectFacts) + if callerOnlyNeeds&possibleFacts == 0 { + return TypeFactsNone + } + } var facts TypeFacts for _, t := range t.Types() { facts |= c.getTypeFactsWorker(t, callerOnlyNeeds) @@ -31913,8 +35202,7 @@ func (c *Checker) isFunctionObjectType(t *Type) bool { } // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.getSignatures()) != 0 || resolved.getMember("bind") != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) + return c.typeHasCallOrConstructSignatures(t) || c.getMemberOfStructuredTypeEx(t, "bind", false /*instantiate*/) != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { @@ -32269,8 +35557,9 @@ func (c *Checker) getNarrowableTypeForReference(t *Type, reference *ast.Node, ch // control flow analysis an opportunity to narrow it further. For example, for a reference of a type // parameter type 'T extends string | undefined' with a contextual type 'string', we substitute // 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'. - substituteConstraints := checkMode&CheckModeInferential == 0 && someType(t, c.isGenericTypeWithUnionConstraint) && (c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode)) + substituteConstraints := checkMode&CheckModeInferential == 0 && c.someGenericTypeWithUnionConstraint(t) && (c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode)) if substituteConstraints { + c.flowMemo.taint(0) return c.mapType(t, c.getBaseConstraintOrType) } return t @@ -32286,6 +35575,13 @@ func (c *Checker) isConstraintPosition(t *Type, node *ast.Node) bool { ast.IsElementAccessExpression(parent) && parent.Expression() == node && !(someType(t, c.isGenericTypeWithoutNullableConstraint) && c.isGenericIndexType(c.getTypeOfExpression(parent.AsElementAccessExpression().ArgumentExpression))) } +func (c *Checker) someGenericTypeWithUnionConstraint(t *Type) bool { + if t.flags&TypeFlagsUnion != 0 { + return core.Some(t.AsUnionType().types, c.isGenericTypeWithUnionConstraint) + } + return c.isGenericTypeWithUnionConstraint(t) +} + func (c *Checker) isGenericTypeWithUnionConstraint(t *Type) bool { if t.flags&TypeFlagsIntersection != 0 { return core.Some(t.AsIntersectionType().types, c.isGenericTypeWithUnionConstraint) diff --git a/tsc/internal/checker/checker_test.go b/tsc/internal/checker/checker_test.go index 42ae368d35924..2fe0a34c8e63b 100644 --- a/tsc/internal/checker/checker_test.go +++ b/tsc/internal/checker/checker_test.go @@ -3,6 +3,7 @@ package checker_test import ( "fmt" "path/filepath" + "strconv" "strings" "testing" @@ -117,6 +118,51 @@ export type E = D;`, assert.Equal(t, defaultClause, defaultReference) } +func TestUnionPropertyFunctionAugmentation(t *testing.T) { + t.Parallel() + for _, lookupFirst := range []bool{false, true} { + t.Run(strconv.FormatBool(lookupFirst), func(t *testing.T) { + t.Parallel() + fs := bundled.WrapFS(vfstest.FromMap(map[string]string{ + "/main.ts": `type Callable = { (): string; shared: string }; +type Other = { call: number; shared: number }; +declare const instance: Callable | Other; +instance;`, + "/function.d.ts": `interface Function { + call(thisArg: any, ...args: any[]): any; +} +interface CallableFunction extends Function {}`, + "/tsconfig.json": `{"compilerOptions":{"strict":true,"noLib":true},"files":["main.ts","function.d.ts"]}`, + }, tspath.CaseInsensitive)) + host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil) + parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, tsoptions.NewDefaultParseConfigHost(fs), nil) + assert.Equal(t, len(errors), 0) + program := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host}) + program.BindSourceFiles() + c, done := program.GetTypeChecker(t.Context()) + defer done() + file := program.GetSourceFile("/main.ts") + typ := c.GetTypeAtLocation(file.Statements.Nodes[3].Expression()) + var call *ast.Symbol + if lookupFirst { + call = c.GetPropertyOfType(typ, "call") + assert.Assert(t, call != nil) + } + properties := c.GetPropertiesOfType(typ) + assert.Equal(t, len(properties), 1) + assert.Equal(t, properties[0].Name(), "shared") + if call == nil { + call = c.GetPropertyOfType(typ, "call") + assert.Assert(t, call != nil) + } + assert.Equal(t, c.GetPropertyOfType(typ, "call"), call) + assert.Equal(t, c.GetPropertyOfType(typ, "shared"), properties[0]) + assert.Equal(t, len(c.GetPropertiesOfType(typ)), 1) + assert.Equal(t, len(c.GetDiagnostics(t.Context(), file)), 0) + }) + } +} + func BenchmarkNewChecker(b *testing.B) { fs := bundled.WrapFS(osvfs.FS()) rootPath := tspath.RootedDirectoryPathFromAbsolute(filepath.Join(repo.TestDataPath(), "fixtures/compiler")) diff --git a/tsc/internal/checker/emitresolver.go b/tsc/internal/checker/emitresolver.go index f0058eb2b28e3..e4fad5c3f5b1f 100644 --- a/tsc/internal/checker/emitresolver.go +++ b/tsc/internal/checker/emitresolver.go @@ -1,7 +1,6 @@ package checker import ( - "maps" "slices" "sync" @@ -269,7 +268,7 @@ func (r *EmitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaratio r.checkerMu.Lock() defer r.checkerMu.Unlock() exports := r.checker.getExportsOfModule(file.Symbol) - for s := range maps.Values(exports) { + for s := range exports.Values() { merged := r.checker.getMergedSymbol(s) if merged != s { if len(merged.Declarations()) > 0 { @@ -753,7 +752,7 @@ func (r *EmitResolver) CreateLateBoundIndexSignatures(container *ast.Node, enclo instanceIndexSymbol := r.checker.getIndexSymbol(sym) var instanceInfos []*IndexInfo if instanceIndexSymbol != nil { - siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym))) + siblingSymbols := slices.Collect(r.checker.getMembersOfSymbol(sym).Values()) instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) } diff --git a/tsc/internal/checker/emitsupport.go b/tsc/internal/checker/emitsupport.go index 3da782c9ab4c4..250223713a6c6 100644 --- a/tsc/internal/checker/emitsupport.go +++ b/tsc/internal/checker/emitsupport.go @@ -292,7 +292,6 @@ func (c *Checker) requiresAddingImplicitUndefined(declaration *ast.Node, symbol symbol = c.getSymbolOfDeclaration(declaration) } t := c.getTypeOfSymbol(symbol) - c.mappedSymbolLinks.Has(symbol) return (symbol.Flags()&ast.SymbolFlagsProperty != 0) && (symbol.Flags()&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && c.ReverseMappedSymbolLinks.Has(symbol) && c.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(c, t) case ast.KindParameter, ast.KindJSDocParameterTag: return c.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration) diff --git a/tsc/internal/checker/exports.go b/tsc/internal/checker/exports.go index bce0ba48cfa20..46dfcc8e604fe 100644 --- a/tsc/internal/checker/exports.go +++ b/tsc/internal/checker/exports.go @@ -1,7 +1,6 @@ package checker import ( - "maps" "slices" "github.com/microsoft/TypeScript/tsc/internal/ast" @@ -188,7 +187,7 @@ func (c *Checker) WasCanceled() bool { } func (c *Checker) GetSignaturesOfType(t *Type, kind SignatureKind) []*Signature { - return c.getSignaturesOfType(t, kind) + return c.resolveExportedSignatures(c.getSignaturesOfType(t, kind)) } func (c *Checker) GetDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { @@ -272,7 +271,7 @@ func (c *Checker) GetExpandedParameters(signature *Signature, skipUnionExpanding } func (c *Checker) GetResolvedSignature(node *ast.Node) *Signature { - return c.getResolvedSignature(node, nil, CheckModeNormal) + return c.resolveExportedSignature(c.getResolvedSignature(node, nil, CheckModeNormal)) } // Return the type of the given property in the given type, or nil if no such property exists @@ -365,8 +364,7 @@ func (c *Checker) GetIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) GetIndexInfosOfIndexSymbol(symbol *ast.Symbol) []*IndexInfo { var siblingSymbols []*ast.Symbol if symbol.Parent() != nil { - siblingSymbols = slices.Collect(maps.Values(c.getMembersOfSymbol(symbol.Parent()))) - c.sortSymbols(siblingSymbols) + siblingSymbols = slices.Collect(c.getMembersOfSymbol(symbol.Parent()).Values()) } return c.getIndexInfosOfIndexSymbol(symbol, siblingSymbols) } diff --git a/tsc/internal/checker/flow.go b/tsc/internal/checker/flow.go index d2831231772fe..5418cc0aeb892 100644 --- a/tsc/internal/checker/flow.go +++ b/tsc/internal/checker/flow.go @@ -2,9 +2,11 @@ package checker import ( "math" + "runtime" "slices" "strconv" "strings" + "unsafe" "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/binder" @@ -33,8 +35,12 @@ func (c *Checker) newFlowType(t *Type, incomplete bool) FlowType { } type SharedFlow struct { - flow *ast.FlowNode - flowType FlowType + flow *ast.FlowNode + flowType FlowType + transient uint32 + reference uint32 + height uint16 + flags uint8 } type FlowState struct { @@ -46,6 +52,13 @@ type FlowState struct { depth int sharedFlowStart int reduceLabels []*ast.FlowReduceLabelData + memoKey CacheHashKey + memoKeyState uint8 + walkFloor uint32 + memoUsed bool + memoAborted bool + memoFaithful bool + impureShared uint32 next *FlowState } @@ -94,8 +107,21 @@ func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Ty f.initialType = core.Coalesce(initialType, declaredType) f.flowContainer = flowContainer f.sharedFlowStart = len(c.sharedFlows) + f.walkFloor = c.flowMemo.nextSerial() c.flowInvocationCount++ - evolvedType := c.getTypeAtFlowNode(f, flowNode).t + c.flowMemoKey(f) + savedHeight := c.flowMemo.saveHeight() + flowType := c.getTypeAtFlowNode(f, flowNode) + if f.memoAborted { + c.sharedFlows = c.sharedFlows[:f.sharedFlowStart] + f.memoAborted = false + f.memoUsed = false + f.memoFaithful = true + f.impureShared = 0 + flowType = c.getTypeAtFlowNode(f, flowNode) + } + c.flowMemo.restoreHeight(savedHeight) + evolvedType := flowType.t c.sharedFlows = c.sharedFlows[:f.sharedFlowStart] c.putFlowState(f) // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, @@ -115,7 +141,12 @@ func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Ty } func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { + entryFlow := flow if f.depth == 2000 { + if f.memoUsed { + f.memoAborted = true + return FlowType{t: c.errorType} + } // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error // and disable further control flow analysis in the containing function or module body. if tr := c.tracer; tr != nil { @@ -125,9 +156,28 @@ func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { c.reportFlowControlError(f.reference) return FlowType{t: c.errorType} } - f.depth++ + entryDepth := f.depth + tracked := f.memoKeyState == 2 + var frame flowFrame + if tracked { + frame = c.flowFrameBegin() + } + hasMemo := tracked && c.inlineLevel == 0 && !c.flowAnalysisDisabled && len(f.reduceLabels) == 0 + var memo frameMemo + if hasMemo { + memo = frameMemo{ + key: f.memoKey, + checkpoints: uint32(len(c.flowMemo.checkpoints)), + } + } + f.depth = entryDepth + 1 var sharedFlow *ast.FlowNode + var steps uint32 + var hit flowMemoHit + var hasHit bool + var t FlowType for { + steps++ flags := flow.Flags if flags&ast.FlowFlagsShared != 0 { // We cache results of flow type resolution for shared nodes that were previously visited in @@ -135,74 +185,133 @@ func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { // antecedent of more than one node. for i := f.sharedFlowStart; i < len(c.sharedFlows); i++ { if c.sharedFlows[i].flow == flow { - f.depth-- - return c.sharedFlows[i].flowType + shared := c.sharedFlows[i] + f.depth = entryDepth + if tracked { + c.flowMemoConsume(shared.transient, shared.reference) + c.flowMemo.addFlags(shared.flags) + c.flowFrameEndWith(frame, shared.height) + } + if hasMemo { + c.flowMemo.checkpoints = c.flowMemo.checkpoints[:memo.checkpoints] + } + return shared.flowType } } sharedFlow = flow } - var t FlowType - switch { - case flags&ast.FlowFlagsAssignment != 0: - t = c.getTypeAtFlowAssignment(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue - } - case flags&ast.FlowFlagsCall != 0: - t = c.getTypeAtFlowCall(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue - } - case flags&ast.FlowFlagsCondition != 0: - t = c.getTypeAtFlowCondition(f, flow) - case flags&ast.FlowFlagsSwitchClause != 0: - t = c.getTypeAtSwitchClause(f, flow) - case flags&ast.FlowFlagsBranchLabel != 0: - antecedents := getBranchLabelAntecedents(flow, f.reduceLabels) - if antecedents.Next == nil { - flow = antecedents.Flow - continue - } - t = c.getTypeAtFlowBranchLabel(f, flow, antecedents) - case flags&ast.FlowFlagsLoopLabel != 0: - if flow.Antecedents.Next == nil { - flow = flow.Antecedents.Flow - continue + if hasMemo { + memoHit, endsIteration, ok := c.flowMemoAtNode(f, entryFlow, flow, flags, memo.key, entryDepth) + if ok { + if !endsIteration { + sharedFlow = nil + } + hit = memoHit + hasHit = true + t = FlowType{t: memoHit.t} + break } - t = c.getTypeAtFlowLoopLabel(f, flow) - case flags&ast.FlowFlagsArrayMutation != 0: - t = c.getTypeAtFlowArrayMutation(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue + } + var next *ast.FlowNode + var done bool + t, next, done = c.getTypeAtFlowNodeStep(f, flow, flags) + if done { + break + } + flow = next + } + f.depth = entryDepth + var taint frameTaint + var height uint16 + if tracked { + if f.memoAborted { + c.flowFrameEnd(frame) + if hasMemo { + c.flowMemo.checkpoints = c.flowMemo.checkpoints[:memo.checkpoints] } - case flags&ast.FlowFlagsReduceLabel != 0: - f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) - t = c.getTypeAtFlowNode(f, flow.Antecedent) - f.reduceLabels = f.reduceLabels[:len(f.reduceLabels)-1] - case flags&ast.FlowFlagsStart != 0: - // Check if we should continue with the control flow of the containing function. - container := flow.Node - if container != nil && container != f.flowContainer && !ast.IsPropertyAccessExpression(f.reference) && !ast.IsElementAccessExpression(f.reference) && !(f.reference.Kind == ast.KindThisKeyword && !ast.IsArrowFunction(container)) { - flow = container.FlowNodeData().FlowNode - continue - } - // At the top of the flow we have the initial type. - t = FlowType{t: f.initialType} - default: - // Unreachable code errors are reported in the binding phase. Here we - // simply return the non-auto declared type to reduce follow-on errors. - t = FlowType{t: c.convertAutoToAny(f.declaredType)} + return t + } + if hasHit { + f.memoUsed = true + c.flowMemo.raiseHeight(hit.height) } + taint, height = c.flowFrameEnd(frame) if sharedFlow != nil { - // Record visited node and the associated type in the cache. - c.sharedFlows = append(c.sharedFlows, SharedFlow{flow: sharedFlow, flowType: t}) + if taint.transient != flowMemoUnTainted { + f.impureShared++ + } + c.sharedFlows = append(c.sharedFlows, SharedFlow{ + flow: sharedFlow, + flowType: t, + transient: taint.transient, + reference: taint.reference, + height: height, + flags: taint.flags, + }) } - f.depth-- - return t + if hasMemo { + c.flowMemoFill(f, memo, entryFlow, flow, t, taint, height, steps, hasHit) + c.flowMemo.checkpoints = c.flowMemo.checkpoints[:memo.checkpoints] + } + } else if sharedFlow != nil { + // Record visited node and the associated type in the cache. + c.sharedFlows = append(c.sharedFlows, SharedFlow{flow: sharedFlow, flowType: t}) + } + return t +} + +func (c *Checker) getTypeAtFlowNodeStep(f *FlowState, flow *ast.FlowNode, flags ast.FlowFlags) (FlowType, *ast.FlowNode, bool) { + var t FlowType + switch { + case flags&ast.FlowFlagsAssignment != 0: + t = c.getTypeAtFlowAssignment(f, flow) + if t.isNil() { + return FlowType{}, flow.Antecedent, false + } + case flags&ast.FlowFlagsCall != 0: + t = c.getTypeAtFlowCall(f, flow) + if t.isNil() { + return FlowType{}, flow.Antecedent, false + } + case flags&ast.FlowFlagsCondition != 0: + t = c.getTypeAtFlowCondition(f, flow) + case flags&ast.FlowFlagsSwitchClause != 0: + t = c.getTypeAtSwitchClause(f, flow) + case flags&ast.FlowFlagsBranchLabel != 0: + antecedents := getBranchLabelAntecedents(flow, f.reduceLabels) + if antecedents.Next == nil { + return FlowType{}, antecedents.Flow, false + } + t = c.getTypeAtFlowBranchLabel(f, flow, antecedents) + case flags&ast.FlowFlagsLoopLabel != 0: + if flow.Antecedents().Next == nil { + return FlowType{}, flow.Antecedents().Flow, false + } + t = c.getTypeAtFlowLoopLabel(f, flow) + case flags&ast.FlowFlagsArrayMutation != 0: + t = c.getTypeAtFlowArrayMutation(f, flow) + if t.isNil() { + return FlowType{}, flow.Antecedent, false + } + case flags&ast.FlowFlagsReduceLabel != 0: + c.flowMemo.taint(0) + f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) + t = c.getTypeAtFlowNode(f, flow.Antecedent) + f.reduceLabels = f.reduceLabels[:len(f.reduceLabels)-1] + case flags&ast.FlowFlagsStart != 0: + // Check if we should continue with the control flow of the containing function. + container := flow.Node + if container != nil && container != f.flowContainer && !ast.IsPropertyAccessExpression(f.reference) && !ast.IsElementAccessExpression(f.reference) && !(f.reference.Kind == ast.KindThisKeyword && !ast.IsArrowFunction(container)) { + return FlowType{}, container.FlowNodeData().FlowNode, false + } + // At the top of the flow we have the initial type. + t = FlowType{t: f.initialType} + default: + // Unreachable code errors are reported in the binding phase. Here we + // simply return the non-auto declared type to reduce follow-on errors. + t = FlowType{t: c.convertAutoToAny(f.declaredType)} } + return t, nil, true } func getBranchLabelAntecedents(flow *ast.FlowNode, reduceLabels []*ast.FlowReduceLabelData) *ast.FlowList { @@ -214,7 +323,7 @@ func getBranchLabelAntecedents(flow *ast.FlowNode, reduceLabels []*ast.FlowReduc return data.Antecedents } } - return flow.Antecedents + return flow.Antecedents() } func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) FlowType { @@ -227,6 +336,9 @@ func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) Flow } if getAssignmentTargetKind(node) == AssignmentKindCompound { flowType := c.getTypeAtFlowNode(f, flow.Antecedent) + if f.memoAborted { + return flowType + } return c.newFlowType(c.getBaseTypeOfLiteralType(flowType.t), flowType.incomplete) } if f.declaredType == c.autoType || f.declaredType == c.autoArrayType { @@ -259,7 +371,8 @@ func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) Flow // A matching dotted name might also be an expando property on a function *expression*, // in which case we continue control flow analysis back to the function's declaration if ast.IsVariableDeclaration(node) && (ast.IsInJSFile(node) || ast.IsVarConstLike(node)) { - if init := node.Initializer(); init != nil && ast.IsFunctionExpressionOrArrowFunction(init) { + init := node.Initializer() + if init != nil && ast.IsFunctionExpressionOrArrowFunction(init) { return c.getTypeAtFlowNode(f, flow.Antecedent) } } @@ -267,7 +380,11 @@ func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) Flow } // for (const _ in ref) acts as a nonnull on ref if ast.IsVariableDeclaration(node) && ast.IsForInStatement(node.Parent.Parent) && (c.isMatchingReference(f.reference, node.Parent.Parent.Expression()) || c.optionalChainContainsReference(node.Parent.Parent.Expression(), f.reference)) { - return FlowType{t: c.getNonNullableTypeIfNeeded(c.finalizeEvolvingArrayType(c.getTypeAtFlowNode(f, flow.Antecedent).t))} + flowType := c.getTypeAtFlowNode(f, flow.Antecedent) + if f.memoAborted { + return flowType + } + return FlowType{t: c.getNonNullableTypeIfNeeded(c.finalizeEvolvingArrayType(flowType.t))} } // Assignment doesn't affect reference return FlowType{} @@ -275,8 +392,10 @@ func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) Flow func (c *Checker) getInitialOrAssignedType(f *FlowState, flow *ast.FlowNode) *Type { if ast.IsVariableDeclaration(flow.Node) || ast.IsBindingElement(flow.Node) { + c.flowMemo.taintReference(f.walkFloor) return c.getNarrowableTypeForReference(c.getInitialType(flow.Node), f.reference, CheckModeNormal) } + c.flowMemo.taintReference(f.walkFloor) return c.getNarrowableTypeForReference(c.getAssignedType(flow.Node), f.reference, CheckModeNormal) } @@ -291,6 +410,9 @@ func (c *Checker) getTypeAtFlowCall(f *FlowState, flow *ast.FlowNode) FlowType { predicate := c.getTypePredicateOfSignature(signature) if predicate != nil && (predicate.kind == TypePredicateKindAssertsThis || predicate.kind == TypePredicateKindAssertsIdentifier) { flowType := c.getTypeAtFlowNode(f, flow.Antecedent) + if f.memoAborted { + return flowType + } t := c.finalizeEvolvingArrayType(flowType.t) var narrowedType *Type switch { @@ -353,7 +475,7 @@ func (c *Checker) narrowTypeByAssertion(f *FlowState, t *Type, expr *ast.Node) * func (c *Checker) getTypeAtFlowCondition(f *FlowState, flow *ast.FlowNode) FlowType { flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - if flowType.t.flags&TypeFlagsNever != 0 { + if f.memoAborted || flowType.t.flags&TypeFlagsNever != 0 { return flowType } // If we have an antecedent type (meaning we're reachable in some way), we first @@ -387,7 +509,10 @@ func (c *Checker) narrowType(f *FlowState, t *Type, expr *ast.Node, assumeTrue b symbol := c.getResolvedSymbol(expr) if c.isConstantVariable(symbol) { declaration := symbol.ValueDeclaration() - if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && c.isConstantReference(f.reference) { + if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && c.isConstantReferenceOfWalk(f) { + if c.inlineLevel == 1 { + c.flowMemo.taint(0) + } c.inlineLevel++ result := c.narrowType(f, t, declaration.Initializer(), assumeTrue) c.inlineLevel-- @@ -1060,6 +1185,9 @@ func (c *Checker) getTypeAtSwitchClause(f *FlowState, flow *ast.FlowNode) FlowTy data := flow.Node.AsFlowSwitchClauseData() expr := ast.SkipParentheses(data.SwitchStatement.Expression()) flowType := c.getTypeAtFlowNode(f, flow.Antecedent) + if f.memoAborted { + return flowType + } t := flowType.t switch { case c.isMatchingReference(f.reference, expr): @@ -1263,7 +1391,10 @@ func (c *Checker) getTypeAtFlowBranchLabel(f *FlowState, flow *ast.FlowNode, ant continue } flowType := c.getTypeAtFlowNode(f, antecedent) - // If the type at a particular antecedent path is the declared type and the + if f.memoAborted { + c.antecedentTypes = c.antecedentTypes[:antecedentStart] + return flowType + } // reference is known to always be assigned (i.e. when declared and initial types // are the same), there is no reason to process more antecedents since the only // possible outcome is subtypes that will be removed in the final union type anyway. @@ -1286,7 +1417,10 @@ func (c *Checker) getTypeAtFlowBranchLabel(f *FlowState, flow *ast.FlowNode, ant } if bypassFlow != nil { flowType := c.getTypeAtFlowNode(f, bypassFlow) - // If the bypass flow contributes a type we haven't seen yet and the switch statement + if f.memoAborted { + c.antecedentTypes = c.antecedentTypes[:antecedentStart] + return flowType + } // isn't exhaustive, process the bypass flow type. Since exhaustiveness checks increase // the risk of circularities, we only want to perform them when they make a difference. if flowType.t.flags&TypeFlagsNever == 0 && !slices.Contains(c.antecedentTypes[antecedentStart:], flowType.t) && !c.isExhaustiveSwitchStatement(bypassFlow.Node.AsFlowSwitchClauseData().SwitchStatement) { @@ -1346,32 +1480,36 @@ func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowT // path that leads to the top. for _, loopInfo := range c.flowLoopStack { if loopInfo.key == key && len(loopInfo.types) != 0 { + c.flowMemo.taint(loopInfo.serial) return c.newFlowType(c.getUnionOrEvolvingArrayType(f, loopInfo.types, UnionReductionLiteral), true /*incomplete*/) } } - // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. antecedentTypes := make([]*Type, 0, 4) subtypeReduction := false var firstAntecedentType FlowType - for list := flow.Antecedents; list != nil; list = list.Next { + for list := flow.Antecedents(); list != nil; list = list.Next { var flowType FlowType if firstAntecedentType.isNil() { // The first antecedent of a loop junction is always the non-looping control // flow path that leads to the top. firstAntecedentType = c.getTypeAtFlowNode(f, list.Flow) + if f.memoAborted { + return firstAntecedentType + } flowType = firstAntecedentType } else { // All but the first antecedent are the looping control flow paths that lead // back to the loop junction. We track these on the flow loop stack. - c.flowLoopStack = append(c.flowLoopStack, FlowLoopInfo{key: key, types: antecedentTypes}) - saveFlowTypeCache := c.flowTypeCache - c.flowTypeCache = nil + serial := c.flowMemo.nextSerial() + c.flowLoopStack = append(c.flowLoopStack, FlowLoopInfo{key: key, types: antecedentTypes, serial: serial}) + saveFlowTypeCache := c.takeFlowTypeCache() flowType = c.getTypeAtFlowNode(f, list.Flow) - c.flowTypeCache = saveFlowTypeCache + c.restoreFlowTypeCache(saveFlowTypeCache) c.flowLoopStack = c.flowLoopStack[:len(c.flowLoopStack)-1] - // If we see a value appear in the cache it is a sign that control flow analysis - // was restarted and completed by checkExpressionCached. We can simply pick up + if f.memoAborted { + return flowType + } // the resulting type and bail out. if cached := c.flowLoopCache[key]; cached != nil { return FlowType{t: cached} @@ -1412,6 +1550,9 @@ func (c *Checker) getTypeAtFlowArrayMutation(f *FlowState, flow *ast.FlowNode) F } if c.isMatchingReference(f.reference, c.getReferenceCandidate(expr)) { flowType := c.getTypeAtFlowNode(f, flow.Antecedent) + if f.memoAborted { + return flowType + } if flowType.t.objectFlags&ObjectFlagsEvolvingArray != 0 { evolvedType := flowType.t if ast.IsCallExpression(node) { @@ -1619,11 +1760,12 @@ func (c *Checker) isMatchingReference(source *ast.Node, target *ast.Node) bool { case ast.KindNonNullExpression, ast.KindParenthesizedExpression, ast.KindSatisfiesExpression: return c.isMatchingReference(source.Expression(), target) case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: + if !ast.IsAccessExpression(target) { + return false + } if sourcePropertyName, ok := c.getAccessedPropertyName(source); ok { - if ast.IsAccessExpression(target) { - if targetPropertyName, ok := c.getAccessedPropertyName(target); ok { - return targetPropertyName == sourcePropertyName && c.isMatchingReference(source.Expression(), target.Expression()) - } + if targetPropertyName, ok := c.getAccessedPropertyName(target); ok { + return targetPropertyName == sourcePropertyName && c.isMatchingReference(source.Expression(), target.Expression()) } } if ast.IsElementAccessExpression(source) && ast.IsElementAccessExpression(target) { @@ -1655,6 +1797,16 @@ var nonDottedNameCacheKey = CacheHashKey(xxh3.HashString128("?")) // leftmost identifier followed by zero or more property names separated by dots. // The result is nonDottedNameCacheKey if the reference isn't a dotted name. func (c *Checker) getFlowReferenceKey(f *FlowState) CacheHashKey { + if runtime.GOARCH == "wasm" { + b := acquireKeyBuilder() + if c.writeFlowCacheKey(b, f.reference, f.declaredType, f.initialType, f.flowContainer) { + return b.releaseHash() + } + b.inlineLength = 0 + b.overflowBuffer = b.overflowBuffer[:0] + wasmKeyBuilderPool = append(wasmKeyBuilderPool, b) + return nonDottedNameCacheKey + } var b keyBuilder if c.writeFlowCacheKey(&b, f.reference, f.declaredType, f.initialType, f.flowContainer) { return b.hash() @@ -1811,6 +1963,13 @@ func (c *Checker) getLiteralPropertyNameText(name *ast.Node) (string, bool) { return "", false } +func (c *Checker) isConstantReferenceOfWalk(f *FlowState) bool { + if ast.IsAccessExpression(f.reference) { + c.flowMemo.taintReference(f.walkFloor) + } + return c.isConstantReference(f.reference) +} + func (c *Checker) isConstantReference(node *ast.Node) bool { switch node.Kind { case ast.KindThisKeyword: @@ -2046,7 +2205,7 @@ func (c *Checker) getTypeOfSwitchClause(clause *ast.Node) *Type { func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { links := c.signatureLinks.Get(node) - signature := links.effectsSignature + signature := links.effectsOrDecoratorSignature if signature == nil { // A call expression parented by an expression statement is a potential assertion. Other call // expressions are potential type predicate function calls. In order to avoid triggering @@ -2079,7 +2238,7 @@ func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { if !(signature != nil && c.hasTypePredicateOrNeverReturnType(signature)) { signature = c.unknownSignature } - links.effectsSignature = signature + links.effectsOrDecoratorSignature = signature } if signature == c.unknownSignature { return nil @@ -2163,7 +2322,7 @@ func (c *Checker) getExplicitTypeOfSymbol(symbol *ast.Symbol, diagnostic *ast.Di } if symbol.Flags()&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 { if symbol.CheckFlags()&ast.CheckFlagsMapped != 0 { - origin := c.mappedSymbolLinks.Get(symbol).syntheticOrigin + origin := c.valueSymbolLinks.TryGet(symbol).target if origin != nil && c.getExplicitTypeOfSymbol(origin, diagnostic) != nil { return c.getTypeOfSymbol(symbol) } @@ -2510,10 +2669,26 @@ func (c *Checker) getFlowTypeInStaticBlocks(symbol *ast.Symbol, staticBlocks []* return nil } +type flowReachableSlot struct { + flow *ast.FlowNode + gen uint32 + val bool +} + func (c *Checker) isReachableFlowNode(flow *ast.FlowNode) bool { + if flow == c.lastFlowNode { + return c.lastFlowNodeReachable + } + slot := &c.flowReachableSlots[(uintptr(unsafe.Pointer(flow))>>3)&2047] + if slot.flow == flow && slot.gen == c.flowReachableGen { + c.lastFlowNode = flow + c.lastFlowNodeReachable = slot.val + return slot.val + } f := c.getFlowState() result := c.isReachableFlowNodeWorker(f, flow, false /*noCacheCheck*/) c.putFlowState(f) + *slot = flowReachableSlot{flow: flow, gen: c.flowReachableGen, val: result} c.lastFlowNode = flow c.lastFlowNodeReachable = result return result @@ -2525,17 +2700,16 @@ func (c *Checker) isReachableFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no return c.lastFlowNodeReachable } flags := flow.Flags - if flags&ast.FlowFlagsShared != 0 { - if !noCacheCheck && len(f.reduceLabels) == 0 { - if reachable, ok := c.flowNodeReachable[flow]; ok { - return reachable - } - reachable := c.isReachableFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodeReachable[flow] = reachable - return reachable + if flags&ast.FlowFlagsShared != 0 && !noCacheCheck && len(f.reduceLabels) == 0 { + slot := &c.flowReachableSlots[(uintptr(unsafe.Pointer(flow))>>3)&2047] + if slot.flow == flow && slot.gen == c.flowReachableGen { + return slot.val } - noCacheCheck = false + reachable := c.isReachableFlowNodeWorker(f, flow, true /*noCacheCheck*/) + *slot = flowReachableSlot{flow: flow, gen: c.flowReachableGen, val: reachable} + return reachable } + noCacheCheck = false switch { case flags&(ast.FlowFlagsAssignment|ast.FlowFlagsCondition|ast.FlowFlagsArrayMutation) != 0: flow = flow.Antecedent @@ -2560,11 +2734,11 @@ func (c *Checker) isReachableFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no } return false case flags&ast.FlowFlagsLoopLabel != 0: - if flow.Antecedents == nil { + if flow.Antecedents() == nil { return false } // A loop is reachable if the control flow path that leads to the top is reachable. - flow = flow.Antecedents.Flow + flow = flow.Antecedents().Flow case flags&ast.FlowFlagsSwitchClause != 0: // The control flow path representing an unmatched value in a switch statement with // no default clause is unreachable if the switch statement is exhaustive. @@ -2613,11 +2787,14 @@ func (c *Checker) isPostSuperFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no flags := flow.Flags if flags&ast.FlowFlagsShared != 0 { if !noCacheCheck { - if postSuper, ok := c.flowNodePostSuper[flow]; ok { - return postSuper + gen := c.flowReachableGen + 1 + slot := &c.flowReachableSlots[(uintptr(unsafe.Pointer(flow))>>3)&2047] + if slot.flow == flow && slot.gen == gen { + return slot.val } postSuper := c.isPostSuperFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodePostSuper[flow] = postSuper + *slot = flowReachableSlot{flow: flow, gen: gen, val: postSuper} + return postSuper } noCacheCheck = false } @@ -2638,7 +2815,7 @@ func (c *Checker) isPostSuperFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no return true case flags&ast.FlowFlagsLoopLabel != 0: // A loop is post-super if the control flow path that leads to the top is post-super. - flow = flow.Antecedents.Flow + flow = flow.Antecedents().Flow case flags&ast.FlowFlagsReduceLabel != 0: f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) result := c.isPostSuperFlowNodeWorker(f, flow.Antecedent, false /*noCacheCheck*/) diff --git a/tsc/internal/checker/flowmemo.go b/tsc/internal/checker/flowmemo.go new file mode 100644 index 0000000000000..5ee248f323066 --- /dev/null +++ b/tsc/internal/checker/flowmemo.go @@ -0,0 +1,567 @@ +package checker + +import ( + "math/bits" + "unsafe" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/zeebo/xxh3" +) + +// Persistent memo of control flow analysis results across getFlowTypeOfReferenceEx walks. +// +// Go walks the flow graph backwards from each reference (getFlowTypeOfReference) and caches only +// inside one walk (sharedFlows) and, per reference key, at loop labels (flowLoopCache). So N +// references to the same variable or property path in a long function each walk the prefix of the +// graph above them, and N distinct properties read from a narrowed object each re-evaluate the +// narrowing of that object. +// +// The memo caches a pure sub-walk of getTypeAtFlowNode(f, flow) by (flow, memoKey(f)): +// - no transient taint: no in-process loop label (Go's incomplete types), circular or in-progress +// resolution, reduce label (try/finally), depth or inline-level limit, and no value cached +// while one of those was active; +// - no reference taint: nothing read from the reference node beyond its key (its position, for +// constraint substitution; the property symbol a property access resolved to); +// - the result is complete, and the frame did not touch the instantiation counters or +// create a type, symbol, or signature. +// +// With caches that only grow, a later walk of the same frame takes a subset of the paths this one +// took, so it does not touch them either; a frame that read flowTypeCache (which is reset) is +// used only while the same cache is current. + +const ( + flowDepthLimit = 2000 + flowMemoUnTainted uint32 = 0xffff_ffff + flowMemoTableBits = 12 + flowMemoTableSize = 1 << flowMemoTableBits + flowMemoFlagEffects uint8 = 1 + flowMemoFlagTypeCache uint8 = 2 + flowMemoFlagCountReset uint8 = 4 +) + +type flowMemoHit struct { + t *Type + epoch uint32 + height uint16 + countReset bool +} + +type flowMemoSlot struct { + key CacheHashKey + flow *ast.FlowNode + t *Type + epoch uint32 + height uint16 + countReset bool +} + +type flowMemoRegs struct { + transient uint32 + reference uint32 + height uint16 + flags uint8 +} + +var freshFlowMemoRegs = flowMemoRegs{ + transient: flowMemoUnTainted, + reference: flowMemoUnTainted, + height: 0, + flags: 0, +} + +type flowFrame struct { + start uint32 + saved flowMemoRegs + instantiationCount uint32 + totalInstantiationCount uint32 + created uint32 + inInstantiation bool +} + +type frameTaint struct { + transient uint32 + reference uint32 + flags uint8 +} + +func (ft frameTaint) isPure() bool { + return ft.transient == flowMemoUnTainted && ft.reference == flowMemoUnTainted +} + +type frameMemo struct { + key CacheHashKey + checkpoints uint32 +} + +type savedFlowTypeCache struct { + cache map[*ast.Node]*Type + epoch uint32 +} + +type flowMemo struct { + enabled bool + slots *[flowMemoTableSize]flowMemoSlot + serial uint32 + regs flowMemoRegs + typeCacheEpoch uint32 + checkpoints []*ast.FlowNode + keyScratch []byte +} + +func newFlowMemo() flowMemo { + return flowMemo{ + enabled: true, + serial: 1, + regs: freshFlowMemoRegs, + typeCacheEpoch: flowMemoUnTainted, + } +} + +func (m *flowMemo) sourceNow() uint32 { + return m.serial +} + +func (m *flowMemo) nextSerial() uint32 { + s := m.serial + m.serial++ + if m.serial == flowMemoUnTainted { + m.enabled = false + m.serial = 1 + } + return s +} + +func (m *flowMemo) begin(instantiationCount, totalInstantiationCount, created uint32, inInstantiation bool) flowFrame { + frame := flowFrame{ + start: m.nextSerial(), + saved: m.regs, + instantiationCount: instantiationCount, + totalInstantiationCount: totalInstantiationCount, + created: created, + inInstantiation: inInstantiation, + } + m.regs = freshFlowMemoRegs + return frame +} + +func (m *flowMemo) endWith(frame flowFrame, height uint16) frameTaint { + r := m.regs + transient := flowMemoUnTainted + if r.transient < frame.start { + transient = r.transient + } + reference := flowMemoUnTainted + if r.reference < frame.start { + reference = r.reference + } + taint := frameTaint{ + transient: transient, + reference: reference, + flags: r.flags, + } + p := frame.saved + nextHeight := height + 1 + if height == 0xffff { + nextHeight = 0xffff + } + if p.height > nextHeight { + nextHeight = p.height + } + m.regs = flowMemoRegs{ + transient: min(r.transient, p.transient), + reference: min(r.reference, p.reference), + height: nextHeight, + flags: r.flags | p.flags, + } + return taint +} + +func (m *flowMemo) end(frame flowFrame) (frameTaint, uint16) { + height := m.regs.height + return m.endWith(frame, height), height +} + +func (m *flowMemo) raiseHeight(height uint16) { + if height > m.regs.height { + m.regs.height = height + } +} + +func (m *flowMemo) addFlags(flags uint8) { + m.regs.flags |= flags +} + +func (m *flowMemo) saveHeight() uint16 { + saved := m.regs.height + m.regs.height = 0 + return saved +} + +func (m *flowMemo) restoreHeight(saved uint16) { + m.regs.height = saved +} + +func (m *flowMemo) taint(source uint32) { + if source < m.regs.transient { + m.regs.transient = source + } +} + +func (m *flowMemo) taintReference(source uint32) { + if source < m.regs.reference { + m.regs.reference = source + } +} + +func flowMemoSlotIndex(flow *ast.FlowNode, key CacheHashKey) uint32 { + f := uint64(uintptr(unsafe.Pointer(flow))) * 0x9E37_79B9_7F4A_7C15 + h := (key.Lo ^ bits.RotateLeft64(key.Hi, 29) ^ bits.RotateLeft64(f, 17)) * 0xBF58_476D_1CE4_E5B9 + return uint32(h>>(64-flowMemoTableBits)) & (flowMemoTableSize - 1) +} + +func (m *flowMemo) lookup(flow *ast.FlowNode, key CacheHashKey) (flowMemoHit, bool) { + if m.slots == nil { + return flowMemoHit{}, false + } + slot := &m.slots[flowMemoSlotIndex(flow, key)] + if slot.flow == flow && slot.key == key && slot.t != nil { + return flowMemoHit{ + t: slot.t, + height: slot.height, + epoch: slot.epoch, + countReset: slot.countReset, + }, true + } + return flowMemoHit{}, false +} + +func (m *flowMemo) store(flow *ast.FlowNode, key CacheHashKey, t *Type, height uint16, epoch uint32, countReset bool) { + if m.slots == nil { + m.slots = new([flowMemoTableSize]flowMemoSlot) + } + m.slots[flowMemoSlotIndex(flow, key)] = flowMemoSlot{ + flow: flow, + key: key, + t: t, + height: height, + epoch: epoch, + countReset: countReset, + } +} + +func isFlowMemoCheckpoint(flow *ast.FlowNode) bool { + return (uint64(uintptr(unsafe.Pointer(flow)))*0x9E37_79B9_7F4A_7C15)>>62 == 0 +} + +func (c *Checker) flowMemoCreated() uint32 { + return c.TypeCount + c.SymbolCount + c.SignatureCount +} + +func (c *Checker) flowFrameBegin() flowFrame { + return c.flowMemo.begin( + c.instantiationCount, + c.TotalInstantiationCount, + c.flowMemoCreated(), + len(c.activeMappers) != 0, + ) +} + +func (c *Checker) flowFrameCounters(frame flowFrame) { + if c.TotalInstantiationCount != frame.totalInstantiationCount || + frame.inInstantiation || + c.flowMemoCreated() != frame.created { + c.flowMemo.addFlags(flowMemoFlagEffects) + } + if frame.instantiationCount == 0 && c.instantiationCount != frame.instantiationCount { + c.flowMemo.addFlags(flowMemoFlagCountReset) + } +} + +func (c *Checker) flowFrameEnd(frame flowFrame) (frameTaint, uint16) { + c.flowFrameCounters(frame) + return c.flowMemo.end(frame) +} + +func (c *Checker) flowFrameEndWith(frame flowFrame, height uint16) frameTaint { + c.flowFrameCounters(frame) + return c.flowMemo.endWith(frame, height) +} + +func (c *Checker) flowMemoConsume(transient, reference uint32) { + if transient != flowMemoUnTainted { + c.flowMemoConsumeTransient(transient) + } + if reference != flowMemoUnTainted { + c.flowMemo.taintReference(reference) + } +} + +func (c *Checker) flowMemoConsumeTransient(source uint32) { + live := false + if source != 0 { + for i := range c.flowLoopStack { + if c.flowLoopStack[i].serial == source { + live = true + break + } + } + if !live { + for i := range c.typeResolutions { + if c.typeResolutions[i].serial == source { + live = true + break + } + } + } + } + if live { + c.flowMemo.taint(source) + } else { + c.flowMemo.taint(0) + } +} + +// flowMemoKey computes the walk's memo key once per walk: a packed or hashed representation of +// the reference's structure (root symbol or 'this', the property names of a property access chain), +// the declared and initial types, and the flow container. Only identifiers, 'this', and chains of +// plain property accesses on them get one. Computing it has no side effects: it reads a resolved +// symbol only if one is already cached and assigns no node or symbol IDs. +func (c *Checker) flowMemoKey(f *FlowState) (CacheHashKey, bool) { + switch f.memoKeyState { + case 1: + return CacheHashKey{}, false + case 2: + return f.memoKey, true + } + key, ok := c.computeFlowMemoKey(f) + if ok { + f.memoKey = key + f.memoKeyState = 2 + return key, true + } + f.memoKeyState = 1 + return CacheHashKey{}, false +} + +func flowContainerAddr(n *ast.Node) uint64 { + if n == nil { + return 0 + } + return uint64(uintptr(unsafe.Pointer(&n.Flags))) +} + +func (c *Checker) computeFlowMemoKey(f *FlowState) (CacheHashKey, bool) { + ref := f.reference + declared := uint32(f.declaredType.id) + initial := uint32(f.initialType.id) + switch ref.Kind { + case ast.KindIdentifier: + sym := c.flowMemoRootSymbol(ref) + if sym == nil { + return CacheHashKey{}, false + } + if declared != 0 && initial < (1<<31) { + p := uint64(uintptr(unsafe.Pointer(sym))) + if f.flowContainer == nil { + return CacheHashKey{ + Hi: uint64(declared) | (uint64(initial) << 32), + Lo: p, + }, true + } + fc := flowContainerAddr(f.flowContainer) * 0x9E37_79B9_7F4A_7C15 + return CacheHashKey{ + Hi: uint64(declared) | (uint64(initial) << 32), + Lo: p ^ bits.RotateLeft64(fc, 31), + }, true + } + var b [25]byte + b[0] = 'i' + p := uint64(uintptr(unsafe.Pointer(sym))) + fc := flowContainerAddr(f.flowContainer) + b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8] = byte(p), byte(p>>8), byte(p>>16), byte(p>>24), byte(p>>32), byte(p>>40), byte(p>>48), byte(p>>56) + b[9], b[10], b[11], b[12] = byte(declared), byte(declared>>8), byte(declared>>16), byte(declared>>24) + b[13], b[14], b[15], b[16] = byte(initial), byte(initial>>8), byte(initial>>16), byte(initial>>24) + b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24] = byte(fc), byte(fc>>8), byte(fc>>16), byte(fc>>24), byte(fc>>32), byte(fc>>40), byte(fc>>48), byte(fc>>56) + h := xxh3.Hash128(b[:]) + h.Hi |= 1 << 63 + return CacheHashKey(h), true + case ast.KindThisKeyword: + if declared != 0 && initial < (1<<31) { + if f.flowContainer == nil { + return CacheHashKey{ + Hi: uint64(declared) | (uint64(initial) << 32), + Lo: 0, + }, true + } + fc := flowContainerAddr(f.flowContainer) * 0x9E37_79B9_7F4A_7C15 + return CacheHashKey{ + Hi: uint64(declared) | (uint64(initial) << 32), + Lo: bits.RotateLeft64(fc, 31), + }, true + } + var b [17]byte + b[0] = 't' + fc := flowContainerAddr(f.flowContainer) + b[1], b[2], b[3], b[4] = byte(declared), byte(declared>>8), byte(declared>>16), byte(declared>>24) + b[5], b[6], b[7], b[8] = byte(initial), byte(initial>>8), byte(initial>>16), byte(initial>>24) + b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16] = byte(fc), byte(fc>>8), byte(fc>>16), byte(fc>>24), byte(fc>>32), byte(fc>>40), byte(fc>>48), byte(fc>>56) + h := xxh3.Hash128(b[:]) + h.Hi |= 1 << 63 + return CacheHashKey(h), true + } + return c.computeHashedFlowMemoKey(f) +} + +func (c *Checker) computeHashedFlowMemoKey(f *FlowState) (CacheHashKey, bool) { + buf := c.flowMemo.keyScratch[:0] + var ok bool + buf, ok = c.writeFlowMemoReference(buf, f.reference) + if !ok { + c.flowMemo.keyScratch = buf[:0] + return CacheHashKey{}, false + } + d := uint32(f.declaredType.id) + in := uint32(f.initialType.id) + fc := flowContainerAddr(f.flowContainer) + buf = append(buf, + byte(d), byte(d>>8), byte(d>>16), byte(d>>24), + byte(in), byte(in>>8), byte(in>>16), byte(in>>24), + byte(fc), byte(fc>>8), byte(fc>>16), byte(fc>>24), + byte(fc>>32), byte(fc>>40), byte(fc>>48), byte(fc>>56), + ) + h := xxh3.Hash128(buf) + c.flowMemo.keyScratch = buf[:0] + h.Hi |= 1 << 63 + return CacheHashKey(h), true +} + +func (c *Checker) flowMemoRootSymbol(node *ast.Node) *ast.Symbol { + links := c.symbolNodeLinks.TryGet(node) + if links == nil || links.resolvedSymbol == nil { + return nil + } + sym := links.resolvedSymbol + if sym == c.unknownSymbol || (node.AsIdentifier().Text == "this" && ast.IsThisInTypeQuery(node)) { + return nil + } + return sym +} + +func (c *Checker) writeFlowMemoReference(buf []byte, node *ast.Node) ([]byte, bool) { + switch node.Kind { + case ast.KindIdentifier: + sym := c.flowMemoRootSymbol(node) + if sym == nil { + return buf, false + } + p := uint64(uintptr(unsafe.Pointer(sym))) + buf = append(buf, 'i', + byte(p), byte(p>>8), byte(p>>16), byte(p>>24), + byte(p>>32), byte(p>>40), byte(p>>48), byte(p>>56), + ) + return buf, true + case ast.KindThisKeyword: + buf = append(buf, 't') + return buf, true + case ast.KindParenthesizedExpression, ast.KindNonNullExpression: + return c.writeFlowMemoReference(buf, node.Expression()) + case ast.KindPropertyAccessExpression: + var ok bool + buf, ok = c.writeFlowMemoReference(buf, node.Expression()) + if !ok { + return buf, false + } + if node.Flags&ast.NodeFlagsOptionalChain != 0 { + buf = append(buf, 'p', '?') + } else { + buf = append(buf, 'p') + } + name := node.Name().Text() + n := uint32(len(name)) + buf = append(buf, byte(n), byte(n>>8), byte(n>>16), byte(n>>24)) + buf = append(buf, name...) + return buf, true + default: + return buf, false + } +} + +func (c *Checker) flowMemoConsultOk(f *FlowState, hit flowMemoHit) bool { + cacheOk := hit.epoch == 0 || hit.epoch == c.flowMemo.typeCacheEpoch + countersOk := len(c.activeMappers) == 0 && (!hit.countReset || c.instantiationCount == 0) + return !f.memoFaithful && len(c.flowLoopStack) == 0 && cacheOk && countersOk && f.impureShared == 0 +} + +func (c *Checker) flowMemoAtNode(f *FlowState, entryFlow, flow *ast.FlowNode, flags ast.FlowFlags, key CacheHashKey, entryDepth int) (flowMemoHit, bool, bool) { + endsIteration := flags&(ast.FlowFlagsCondition|ast.FlowFlagsSwitchClause|ast.FlowFlagsArrayMutation|ast.FlowFlagsStart) != 0 || + (flags&(ast.FlowFlagsBranchLabel|ast.FlowFlagsLoopLabel) != 0 && flow.Antecedents() != nil && flow.Antecedents().Next != nil) + if !endsIteration && flow != entryFlow { + if !isFlowMemoCheckpoint(flow) { + return flowMemoHit{}, false, false + } + c.flowMemo.checkpoints = append(c.flowMemo.checkpoints, flow) + } + hit, ok := c.flowMemoLookup(f, flow, key, entryDepth) + if !ok { + return flowMemoHit{}, false, false + } + return hit, endsIteration, true +} + +func (c *Checker) flowMemoLookup(f *FlowState, flow *ast.FlowNode, key CacheHashKey, entryDepth int) (flowMemoHit, bool) { + hit, ok := c.flowMemo.lookup(flow, key) + if !ok { + return flowMemoHit{}, false + } + if !c.flowMemoConsultOk(f, hit) { + return flowMemoHit{}, false + } + if entryDepth+int(hit.height) >= flowDepthLimit { + return flowMemoHit{}, false + } + return hit, true +} + +func (c *Checker) flowMemoFill(f *FlowState, memo frameMemo, entryFlow, finalFlow *ast.FlowNode, t FlowType, taint frameTaint, height uint16, steps uint32, hasHit bool) { + if !taint.isPure() || taint.flags&flowMemoFlagEffects != 0 { + return + } + if t.incomplete || c.flowAnalysisDisabled { + return + } + if height == 0 && steps < 4 && !hasHit { + return + } + epoch := uint32(0) + if taint.flags&flowMemoFlagTypeCache != 0 { + epoch = c.flowMemo.typeCacheEpoch + } + countReset := taint.flags&flowMemoFlagCountReset != 0 + if entryFlow != finalFlow || !hasHit { + c.flowMemo.store(entryFlow, memo.key, t.t, height, epoch, countReset) + } + if finalFlow != entryFlow && !hasHit { + c.flowMemo.store(finalFlow, memo.key, t.t, height, epoch, countReset) + } + for i := int(memo.checkpoints); i < len(c.flowMemo.checkpoints); i++ { + checkpoint := c.flowMemo.checkpoints[i] + if checkpoint != finalFlow { + c.flowMemo.store(checkpoint, memo.key, t.t, height, epoch, countReset) + } + } +} + +func (c *Checker) takeFlowTypeCache() savedFlowTypeCache { + saved := savedFlowTypeCache{ + cache: c.flowTypeCache, + epoch: c.flowMemo.typeCacheEpoch, + } + c.flowTypeCache = nil + c.flowMemo.typeCacheEpoch = c.flowMemo.nextSerial() + return saved +} + +func (c *Checker) restoreFlowTypeCache(saved savedFlowTypeCache) { + c.flowTypeCache = saved.cache + c.flowMemo.typeCacheEpoch = saved.epoch +} diff --git a/tsc/internal/checker/grammarchecks.go b/tsc/internal/checker/grammarchecks.go index 93bed4b355296..b8815acb72d49 100644 --- a/tsc/internal/checker/grammarchecks.go +++ b/tsc/internal/checker/grammarchecks.go @@ -995,13 +995,30 @@ func (c *Checker) checkGrammarForInvalidExclamationToken(postfixToken *ast.Token return postfixToken != nil && postfixToken.Kind == ast.KindExclamationToken && c.grammarErrorOnNode(postfixToken, message) } -func (c *Checker) checkGrammarObjectLiteralExpression(node *ast.ObjectLiteralExpression, inDestructuring bool) bool { - seen := make(map[string]DeclarationMeaning) +func (c *Checker) releaseObjectLiteralSeen(seen map[string]DeclarationMeaning) { + if seen != nil { + clear(seen) + c.freeObjectLiteralSeen = append(c.freeObjectLiteralSeen, seen) + } +} +func (c *Checker) checkGrammarObjectLiteralExpression(node *ast.ObjectLiteralExpression, inDestructuring bool) bool { var properties []*ast.Node if node.Properties != nil { properties = node.Properties.Nodes } + if len(properties) == 0 { + return false + } + var seen map[string]DeclarationMeaning + if !inDestructuring && len(properties) > 1 { + if n := len(c.freeObjectLiteralSeen); n > 0 { + seen = c.freeObjectLiteralSeen[n-1] + c.freeObjectLiteralSeen = c.freeObjectLiteralSeen[:n-1] + } else { + seen = make(map[string]DeclarationMeaning) + } + } for _, prop := range properties { if prop.Kind == ast.KindSpreadAssignment { spreadAssignment := prop.AsSpreadAssignment() @@ -1105,7 +1122,7 @@ func (c *Checker) checkGrammarObjectLiteralExpression(node *ast.ObjectLiteralExp panic(fmt.Sprintf("Unexpected node kind %q", prop.Kind)) } - if !inDestructuring { + if seen != nil { effectiveName, ok := c.getEffectivePropertyNameForPropertyNameNode(name) if !ok { continue @@ -1123,15 +1140,18 @@ func (c *Checker) checkGrammarObjectLiteralExpression(node *ast.ObjectLiteralExp if existingKind != DeclarationMeaningGetOrSetAccessor && currentKind != existingKind { seen[effectiveName] = currentKind | existingKind } else { + c.releaseObjectLiteralSeen(seen) return c.grammarErrorOnNode(name, diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name) } } else { + c.releaseObjectLiteralSeen(seen) return c.grammarErrorOnNode(name, diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name) } } } } + c.releaseObjectLiteralSeen(seen) return false } diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index 02c7d009b9ec5..9614d8a75ed68 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -8,12 +8,22 @@ import ( "github.com/microsoft/TypeScript/tsc/internal/jsnum" ) -type InferenceKey struct { - source TypeId - target TypeId - priority InferencePriority - contravariant bool - bivariant bool +type inferInfoOutcome struct { + candidates []*Type + contraCandidates []*Type + priority InferencePriority + topLevel bool +} + +type inferMemoEntry struct { + infos []inferInfoOutcome + cleared bool +} + +type inferenceVisitedSlot struct { + key uint64 + status InferencePriority + epoch uint32 } type InferenceState struct { @@ -24,35 +34,129 @@ type InferenceState struct { inferencePriority InferencePriority contravariant bool bivariant bool + clearedInferences bool expandingFlags ExpandingFlags propagationType *Type - visited map[InferenceKey]InferencePriority - sourceStack []*Type - targetStack []*Type + visited []inferenceVisitedSlot + visitedCount uint32 + visitedEpoch uint32 + visitedInline [16]inferenceVisitedSlot + sourceStack []relaterStackEntry + targetStack []relaterStackEntry next *InferenceState } func (c *Checker) getInferenceState() *InferenceState { n := c.freeinferenceState if n == nil { - n = &InferenceState{} + n = &InferenceState{visitedEpoch: 1} + n.visited = n.visitedInline[:] + return n } c.freeinferenceState = n.next + n.next = nil return n } func (c *Checker) putInferenceState(n *InferenceState) { - clear(n.visited) - *n = InferenceState{ - inferences: n.inferences[:0], - visited: n.visited, - sourceStack: n.sourceStack[:0], - targetStack: n.targetStack[:0], - next: c.freeinferenceState, - } + epoch := n.visitedEpoch + if n.visitedCount != 0 { + epoch++ + if epoch == 0 { + clear(n.visited) + epoch = 1 + } + } + n.inferences = n.inferences[:0] + n.originalSource = nil + n.originalTarget = nil + n.priority = 0 + n.inferencePriority = 0 + n.contravariant = false + n.bivariant = false + n.clearedInferences = false + n.expandingFlags = 0 + n.propagationType = nil + n.visitedCount = 0 + n.visitedEpoch = epoch + n.sourceStack = n.sourceStack[:0] + n.targetStack = n.targetStack[:0] + n.next = c.freeinferenceState c.freeinferenceState = n } +func (n *InferenceState) lookupOrInsertVisited(key uint64) (InferencePriority, bool) { + slots := n.visited + epoch := n.visitedEpoch + mask := uint32(len(slots) - 1) + idx := uint32((key*0x9e3779b97f4a7c15)>>32) & mask + for { + s := &slots[idx] + if s.epoch != epoch { + if (n.visitedCount+1)*4 >= uint32(len(slots))*3 { + n.growVisited() + slots = n.visited + epoch = n.visitedEpoch + mask = uint32(len(slots) - 1) + idx = uint32((key*0x9e3779b97f4a7c15)>>32) & mask + for slots[idx].epoch == epoch { + idx = (idx + 1) & mask + } + s = &slots[idx] + } + s.key = key + s.status = InferencePriorityCircularity + s.epoch = epoch + n.visitedCount++ + return 0, false + } + if s.key == key { + return s.status, true + } + idx = (idx + 1) & mask + } +} + +func (n *InferenceState) updateVisited(key uint64, status InferencePriority) { + slots := n.visited + epoch := n.visitedEpoch + mask := uint32(len(slots) - 1) + idx := uint32((key*0x9e3779b97f4a7c15)>>32) & mask + for { + s := &slots[idx] + if s.epoch == epoch && s.key == key { + s.status = status + return + } + idx = (idx + 1) & mask + } +} + +func (n *InferenceState) growVisited() { + oldSlots := n.visited + oldEpoch := n.visitedEpoch + newCap := len(oldSlots) * 2 + newSlots := make([]inferenceVisitedSlot, newCap) + mask := uint32(newCap - 1) + for i := range oldSlots { + s := &oldSlots[i] + if s.epoch == oldEpoch { + idx := uint32((s.key*0x9e3779b97f4a7c15)>>32) & mask + for newSlots[idx].epoch != 0 { + idx = (idx + 1) & mask + } + newSlots[idx] = inferenceVisitedSlot{key: s.key, status: s.status, epoch: 1} + } + } + n.visited = newSlots + n.visitedEpoch = 1 +} + +const ( + inferMemoMaxInfos = 8 + inferMemoMinSteps = 16 +) + func (c *Checker) inferTypes(inferences []*InferenceInfo, originalSource *Type, originalTarget *Type, priority InferencePriority, contravariant bool) { n := c.getInferenceState() n.inferences = inferences @@ -61,11 +165,115 @@ func (c *Checker) inferTypes(inferences []*InferenceInfo, originalSource *Type, n.priority = priority n.inferencePriority = InferencePriorityMaxValue n.contravariant = contravariant - c.inferFromTypes(n, originalSource, originalTarget) + c.inferFromTypesMemo(n, originalSource, originalTarget) c.putInferenceState(n) } +func (c *Checker) inferFromTypesMemo(n *InferenceState, source *Type, target *Type) { + if !c.inferLongTargets[target] { + stepsBefore := c.inferMemoSteps + c.inferFromTypes(n, source, target) + if c.inferMemoSteps-stepsBefore >= inferMemoMinSteps { + if c.inferLongTargets == nil { + c.inferLongTargets = make(map[*Type]bool) + } + c.inferLongTargets[target] = true + } + return + } + inferences := n.inferences + if c.skipDirectInferenceNodes.Len() != 0 || len(inferences) > inferMemoMaxInfos { + c.inferFromTypes(n, source, target) + return + } + var b keyBuilder + b.writeUint32(uint32(source.id)) + b.writeUint32(uint32(target.id)) + b.writeUint32(uint32(n.priority)) + b.writeByte(core.IfElse(n.contravariant, byte(1), byte(0))) + b.writeUint32(uint32(len(inferences))) + for _, info := range inferences { + var tpId uint32 + if info.typeParameter != nil { + tpId = uint32(info.typeParameter.id) + } + b.writeUint32(tpId) + b.writeUint32(uint32(info.priority)) + b.writeByte(core.IfElse(info.topLevel, byte(1), byte(0)) | core.IfElse(info.isFixed, byte(2), byte(0))) + b.writeUint32(uint32(info.impliedArity)) + b.writeUint32(uint32(len(info.candidates))) + b.writeTypes(info.candidates) + b.writeUint32(uint32(len(info.contraCandidates))) + b.writeTypes(info.contraCandidates) + } + key := b.hash() + if entry := c.inferMemoEntries[key]; entry != nil { + for i, info := range inferences { + outcome := &entry.infos[i] + info.candidates = append(info.candidates[:0], outcome.candidates...) + info.contraCandidates = append(info.contraCandidates[:0], outcome.contraCandidates...) + info.priority = outcome.priority + info.topLevel = outcome.topLevel + } + if entry.cleared { + clearCachedInferences(inferences) + } + return + } + inInstantiation := len(c.activeMappers) != 0 + resetsBefore := c.instantiationCountResets + cyclesBefore := c.resolutionCycleCount + var hasFailedResolution bool + for i := c.resolutionStart; i < len(c.typeResolutions); i++ { + if !c.typeResolutions[i].result { + hasFailedResolution = true + break + } + } + typeCount := c.TypeCount + symbolCount := c.SymbolCount + sigCount := c.SignatureCount + instCount := c.instantiationCount + totalInstCount := c.TotalInstantiationCount + diagCount := c.diagnosticCount + impureCount := c.unionImpureCount + stepsBefore := c.inferMemoSteps + c.inferFromTypes(n, source, target) + steps := c.inferMemoSteps - stepsBefore + if steps >= inferMemoMinSteps && + !inInstantiation && + !hasFailedResolution && + c.instantiationCountResets == resetsBefore && + c.resolutionCycleCount == cyclesBefore && + c.TypeCount == typeCount && + c.SymbolCount == symbolCount && + c.SignatureCount == sigCount && + c.instantiationCount == instCount && + c.TotalInstantiationCount == totalInstCount && + c.diagnosticCount == diagCount && + c.unionImpureCount == impureCount && + c.skipDirectInferenceNodes.Len() == 0 { + outcomes := make([]inferInfoOutcome, len(inferences)) + for i, info := range inferences { + outcomes[i] = inferInfoOutcome{ + candidates: slices.Clone(info.candidates), + contraCandidates: slices.Clone(info.contraCandidates), + priority: info.priority, + topLevel: info.topLevel, + } + } + if c.inferMemoEntries == nil { + c.inferMemoEntries = make(map[CacheHashKey]*inferMemoEntry) + } + c.inferMemoEntries[key] = &inferMemoEntry{ + infos: outcomes, + cleared: n.clearedInferences, + } + } +} + func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) { + c.inferMemoSteps++ if !c.couldContainTypeVariables(target) || c.isNoInferType(target) { return } @@ -95,19 +303,22 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) return } if target.flags&TypeFlagsUnion != 0 { + var sourceBuf [1]*Type var sourceTypes []*Type if source.flags&TypeFlagsUnion != 0 { sourceTypes = source.Types() } else { - sourceTypes = []*Type{source} + sourceBuf[0] = source + sourceTypes = sourceBuf[:] } + targetTypes := target.Types() // First, infer between identically matching source and target constituents and remove the // matching types. - tempSources, tempTargets := c.inferFromMatchingTypes(n, sourceTypes, target.Types(), (*Checker).isTypeOrBaseIdenticalTo, false /*sort*/) + sources, targets := c.inferFromMatchingTypes(n, sourceTypes, targetTypes, (*Checker).isTypeOrBaseIdenticalTo, false /*sort*/) // Next, infer between closely matching source and target constituents and remove // the matching types. Types closely match when they are instantiations of the same // object type or instantiations of the same type alias. - sources, targets := c.inferFromMatchingTypes(n, tempSources, tempTargets, (*Checker).isTypeCloselyMatchedBy, true /*sort*/) + sources, targets = c.inferFromMatchingTypes(n, sources, targets, (*Checker).isTypeCloselyMatchedBy, true /*sort*/) if len(targets) == 0 { return } @@ -117,22 +328,24 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) // However, simply making no inferences is undesirable because it could ultimately mean // inferring a type parameter constraint. Instead, make a lower priority inference from // the full source to whatever remains in the target. For example, when inferring from - // string to 'string | T', make a lower priority inference of string for T. + // `string` to `string | T`, make a lower priority inference of `string` for `T`. c.inferWithPriority(n, source, target, InferencePriorityNakedTypeVariable) return } source = c.getUnionType(sources) } else if target.flags&TypeFlagsIntersection != 0 && !core.Every(target.Types(), c.isNonGenericObjectType) { // We reduce intersection types unless they're simple combinations of object types. For example, - // when inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and - // infer { extra: any } for T. But when inferring to 'string[] & Iterable' we want to keep the - // string[] on the source side and infer string for T. + // when inferring from `string[] & { extra: any }` to `string[] & T` we want to remove `string[]` and + // infer `{ extra: any }` for `T`. But when inferring to `string[] & Iterable` we want to keep the + // `string[]` on the source side and infer `string` for `T`. if source.flags&TypeFlagsUnion == 0 { + var sourceBuf [1]*Type var sourceTypes []*Type if source.flags&TypeFlagsIntersection != 0 { sourceTypes = source.Types() } else { - sourceTypes = []*Type{source} + sourceBuf[0] = source + sourceTypes = sourceBuf[:] } // Infer between identically matching source and target constituents and remove the matching types. sources, targets := c.inferFromMatchingTypes(n, sourceTypes, target.Types(), (*Checker).isTypeIdenticalTo, false /*sort*/) @@ -182,8 +395,10 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) return } if n.priority < inference.priority { - inference.candidates = nil - inference.contraCandidates = nil + clear(inference.candidates) + inference.candidates = inference.candidates[:0] + clear(inference.contraCandidates) + inference.contraCandidates = inference.contraCandidates[:0] inference.topLevel = true inference.priority = n.priority } @@ -193,15 +408,18 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) if n.contravariant && !n.bivariant { if !slices.Contains(inference.contraCandidates, candidate) { inference.contraCandidates = append(inference.contraCandidates, candidate) + n.clearedInferences = true clearCachedInferences(n.inferences) } } else if !slices.Contains(inference.candidates, candidate) { inference.candidates = append(inference.candidates, candidate) + n.clearedInferences = true clearCachedInferences(n.inferences) } } if n.priority&InferencePriorityReturnType == 0 && target.flags&TypeFlagsTypeParameter != 0 && inference.topLevel && !c.isTypeParameterAtTopLevel(n.originalTarget, target, 0) { inference.topLevel = false + n.clearedInferences = true clearCachedInferences(n.inferences) } } @@ -344,26 +562,30 @@ func (c *Checker) inferFromContravariantTypesIfStrictFunctionTypes(n *InferenceS // such that we would go on inferring forever, even though we would never infer // between the same pair of types. func (c *Checker) invokeOnce(n *InferenceState, source *Type, target *Type, action func(c *Checker, n *InferenceState, source *Type, target *Type)) { - key := InferenceKey{source: source.id, target: target.id, priority: n.priority, contravariant: n.contravariant, bivariant: n.bivariant} - if status, ok := n.visited[key]; ok { + var flags uint64 + if n.contravariant { + flags |= 1 << 11 + } + if n.bivariant { + flags |= 1 << 12 + } + flags |= uint64(n.priority) & 0x7ff + key := uint64(source.id) | (uint64(target.id) << 25) | (flags << 50) + if status, ok := n.lookupOrInsertVisited(key); ok { n.inferencePriority = min(n.inferencePriority, status) return } - if n.visited == nil { - n.visited = make(map[InferenceKey]InferencePriority) - } - n.visited[key] = InferencePriorityCircularity saveInferencePriority := n.inferencePriority n.inferencePriority = InferencePriorityMaxValue // We stop inferring and report a circularity if we encounter duplicate recursion identities on both // the source side and the target side. saveExpandingFlags := n.expandingFlags - n.sourceStack = append(n.sourceStack, source) - n.targetStack = append(n.targetStack, target) - if c.isDeeplyNestedType(source, n.sourceStack, 2) { + n.sourceStack = append(n.sourceStack, relaterStackEntry{t: source}) + n.targetStack = append(n.targetStack, relaterStackEntry{t: target}) + if n.expandingFlags&ExpandingFlagsSource == 0 && c.isDeeplyNestedOnStack(source, n.sourceStack, 2) { n.expandingFlags |= ExpandingFlagsSource } - if c.isDeeplyNestedType(target, n.targetStack, 2) { + if n.expandingFlags&ExpandingFlagsTarget == 0 && c.isDeeplyNestedOnStack(target, n.targetStack, 2) { n.expandingFlags |= ExpandingFlagsTarget } if n.expandingFlags != ExpandingFlagsBoth { @@ -374,13 +596,15 @@ func (c *Checker) invokeOnce(n *InferenceState, source *Type, target *Type, acti n.targetStack = n.targetStack[:len(n.targetStack)-1] n.sourceStack = n.sourceStack[:len(n.sourceStack)-1] n.expandingFlags = saveExpandingFlags - n.visited[key] = n.inferencePriority + n.updateVisited(key, n.inferencePriority) n.inferencePriority = min(n.inferencePriority, saveInferencePriority) } func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, targets []*Type, matches func(c *Checker, s *Type, t *Type) bool, sort bool) ([]*Type, []*Type) { - var matchedSources []*Type - var matchedTargets []*Type + var matchedSourcesBuf [8]*Type + var matchedTargetsBuf [8]*Type + matchedSources := matchedSourcesBuf[:0] + matchedTargets := matchedTargetsBuf[:0] for _, t := range targets { for _, s := range sources { if matches(c, s, t) { @@ -392,13 +616,16 @@ func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, tar } } } + if len(matchedSources) == 0 { + return sources, targets + } if sort { // Sort target types by decreasing depth of generic instantiations. Intuitively, a successful // inference from a type argument with deeper nesting is of higher quality because we've stripped // away more layers of type instantiations that otherwise might skew the results. For example, - // when inferring from string[] | string[][] to T[] | T[][], the inference of string we make from - // relating string[][] to T[][] is of higher quality than the inference of string[] we make relating - // string[][] to T[]. + // when inferring from `string[] | string[][]` to `T[] | T[][]`, the inference of `string` we make from + // relating `string[][]` to `T[][]` is of higher quality than the inference of `string[]` we make relating + // `string[][]` to `T[]`. slices.SortFunc(matchedTargets, compareTypesAndDepth) for _, t := range matchedTargets { for _, s := range matchedSources { @@ -408,13 +635,25 @@ func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, tar } } } - if len(matchedSources) != 0 { - sources = core.Filter(sources, func(t *Type) bool { return !slices.Contains(matchedSources, t) }) + var newSources []*Type + if len(sources) > len(matchedSources) { + newSources = make([]*Type, 0, len(sources)-len(matchedSources)) + for _, s := range sources { + if !slices.Contains(matchedSources, s) { + newSources = append(newSources, s) + } + } } - if len(matchedTargets) != 0 { - targets = core.Filter(targets, func(t *Type) bool { return !slices.Contains(matchedTargets, t) }) + var newTargets []*Type + if len(targets) > len(matchedTargets) { + newTargets = make([]*Type, 0, len(targets)-len(matchedTargets)) + for _, t := range targets { + if !slices.Contains(matchedTargets, t) { + newTargets = append(newTargets, t) + } + } } - return sources, targets + return newSources, newTargets } // Compare two types first by depth and then by the regular type ordering. @@ -461,12 +700,20 @@ func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets if targetFlags&TypeFlagsUnion != 0 { var nakedTypeVariable *Type var sources []*Type + var sourcesBuf [1]*Type if source.flags&TypeFlagsUnion != 0 { sources = source.Types() } else { - sources = []*Type{source} + sourcesBuf[0] = source + sources = sourcesBuf[:] + } + var matchedBuf [64]bool + var matched []bool + if len(sources) <= len(matchedBuf) { + matched = matchedBuf[:len(sources)] + } else { + matched = make([]bool, len(sources)) } - matched := make([]bool, len(sources)) inferenceCircularity := false // First infer to types that are not naked type variables. For each source type we // track whether inferences were made from that particular type to some target with @@ -504,7 +751,8 @@ func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets // types from which no inferences have been made so far and infer from that union to the // naked type variable. if typeVariableCount == 1 && !inferenceCircularity { - var unmatched []*Type + var unmatchedBuf [8]*Type + unmatched := unmatchedBuf[:0] for i, s := range sources { if !matched[i] { unmatched = append(unmatched, s) @@ -569,13 +817,18 @@ func (c *Checker) inferToConditionalType(n *InferenceState, source *Type, target c.inferFromTypes(n, getNonDistributedTypeParameter(c.getTrueTypeFromConditionalType(source)), c.getTrueTypeFromConditionalType(target)) c.inferFromTypes(n, getNonDistributedTypeParameter(c.getFalseTypeFromConditionalType(source)), c.getFalseTypeFromConditionalType(target)) } else { - targetTypes := []*Type{c.getTrueTypeFromConditionalType(target), c.getFalseTypeFromConditionalType(target)} - c.inferToMultipleTypesWithPriority(n, source, targetTypes, target.flags, core.IfElse(n.contravariant, InferencePriorityContravariantConditional, 0)) + var targetTypes [2]*Type + targetTypes[0] = c.getTrueTypeFromConditionalType(target) + targetTypes[1] = c.getFalseTypeFromConditionalType(target) + c.inferToMultipleTypesWithPriority(n, source, targetTypes[:], target.flags, core.IfElse(n.contravariant, InferencePriorityContravariantConditional, 0)) } } func (c *Checker) inferToTemplateLiteralType(n *InferenceState, source *Type, target *TemplateLiteralType) { matches := c.inferTypesFromTemplateLiteralType(source, target, c.compareTypesAssignable) + if matches != nil { + defer c.releaseTemporaryTypeArguments(matches) + } types := target.types // When the target template literal contains only placeholders (meaning that inference is intended to extract // single characters and remainder strings) and inference fails to produce matches, we want to infer 'never' for @@ -837,26 +1090,48 @@ func (c *Checker) inferFromObjectTypes(n *InferenceState, source *Type, target * } func (c *Checker) inferFromProperties(n *InferenceState, source *Type, target *Type) { - properties := c.getPropertiesOfObjectType(target) + properties, lazyTarget := c.getPropertiesOfTypeLazily(target) + resolvedSource := c.isResolvedPlainObjectType(source) for _, targetProp := range properties { - sourceProp := c.getPropertyOfType(source, targetProp.Name()) + if lazyTarget == nil || lazyTarget.objectFlags&ObjectFlagsMapped == 0 { + if links := c.valueSymbolLinks.TryGet(targetProp); links != nil && links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { + continue + } + } + var sourceProp *ast.Symbol + if resolvedSource { + sourceProp = c.getResolvedPropertyOfPlainObjectType(source, targetProp.Name()) + } else { + sourceProp = c.getPropertyOfTypeWorker(source, targetProp.Name(), false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/, false /*instantiate*/) + } if sourceProp != nil && !core.Some(sourceProp.Declarations(), c.isSkipDirectInferenceNode) { - c.inferFromTypes(n, c.removeMissingType(c.getTypeOfSymbol(sourceProp), sourceProp.Flags()&ast.SymbolFlagsOptional != 0), c.removeMissingType(c.getTypeOfSymbol(targetProp), targetProp.Flags()&ast.SymbolFlagsOptional != 0)) + if lazyTarget != nil { + targetProp = c.getPropertyOfType(lazyTarget, targetProp.Name()) + } + targetType := c.removeMissingType(c.getTypeOfSymbol(targetProp), targetProp.Flags()&ast.SymbolFlagsOptional != 0) + if c.couldContainTypeVariables(targetType) && !c.isNoInferType(targetType) { + if !resolvedSource { + sourceProp = c.getPropertyOfType(source, targetProp.Name()) + } + c.inferFromTypes(n, c.removeMissingType(c.getTypeOfSymbol(sourceProp), sourceProp.Flags()&ast.SymbolFlagsOptional != 0), targetType) + } } } } func (c *Checker) inferFromSignatures(n *InferenceState, source *Type, target *Type, kind SignatureKind) { - sourceSignatures := c.getSignaturesOfType(source, kind) - sourceLen := len(sourceSignatures) - if sourceLen > 0 { - // We match source and target signatures from the bottom up, and if the source has fewer signatures - // than the target, we infer from the first source signature to the excess target signatures. - targetSignatures := c.getSignaturesOfType(target, kind) - targetLen := len(targetSignatures) - for i := range targetLen { - sourceIndex := max(sourceLen-targetLen+i, 0) - c.inferFromSignature(n, c.getBaseSignature(sourceSignatures[sourceIndex]), c.getErasedSignature(targetSignatures[i])) + targetSignatures := c.getSignaturesOfType(target, kind) + targetLen := len(targetSignatures) + if targetLen > 0 { + sourceSignatures := c.getSignaturesOfType(source, kind) + sourceLen := len(sourceSignatures) + if sourceLen > 0 { + // We match source and target signatures from the bottom up, and if the source has fewer signatures + // than the target, we infer from the first source signature to the excess target signatures. + for i := range targetLen { + sourceIndex := max(sourceLen-targetLen+i, 0) + c.inferFromSignature(n, c.getBaseSignature(sourceSignatures[sourceIndex]), c.getErasedSignature(targetSignatures[i])) + } } } } @@ -889,26 +1164,28 @@ func (c *Checker) applyToParameterTypes(source *Signature, target *Signature, ca if sourceRestType == nil { paramCount = min(sourceCount, targetNonRestCount) } - sourceThisType := c.getThisTypeOfSignature(source) - if sourceThisType != nil { - targetThisType := c.getThisTypeOfSignature(target) - if targetThisType != nil { + targetThisType := c.getThisTypeOfSignature(target) + if targetThisType != nil && c.couldContainTypeVariables(targetThisType) { + if sourceThisType := c.getThisTypeOfSignature(source); sourceThisType != nil { callback(sourceThisType, targetThisType) } } for i := range paramCount { - callback(c.getTypeAtPosition(source, i), c.getTypeAtPosition(target, i)) + targetParamType := c.getTypeAtPosition(target, i) + if c.couldContainTypeVariables(targetParamType) { + callback(c.getTypeAtPosition(source, i), targetParamType) + } } - if targetRestType != nil { + if targetRestType != nil && c.couldContainTypeVariables(targetRestType) { callback(c.getRestTypeAtPosition(source, paramCount, c.isConstTypeVariable(targetRestType, 0) && !someType(targetRestType, c.isMutableArrayLikeType) /*readonly*/), targetRestType) } } func (c *Checker) applyToReturnTypes(source *Signature, target *Signature, callback func(s *Type, t *Type)) { targetTypePredicate := c.getTypePredicateOfSignature(target) - if targetTypePredicate != nil { + if targetTypePredicate != nil && targetTypePredicate.t != nil && c.couldContainTypeVariables(targetTypePredicate.t) { sourceTypePredicate := c.getTypePredicateOfSignature(source) - if sourceTypePredicate != nil && c.typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.t != nil && targetTypePredicate.t != nil { + if sourceTypePredicate != nil && c.typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.t != nil { callback(sourceTypePredicate.t, targetTypePredicate.t) return } @@ -926,6 +1203,9 @@ func (c *Checker) inferFromIndexTypes(n *InferenceState, source *Type, target *T priority = InferencePriorityHomomorphicMappedType } indexInfos := c.getIndexInfosOfType(target) + if len(indexInfos) == 0 { + return + } if c.isObjectTypeWithInferableIndex(source) { for _, targetInfo := range indexInfos { var propTypes []*Type @@ -1117,9 +1397,14 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { if indexInfo != nil { indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil, nil)} } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() limitedConstraint := c.getLimitedConstraint(t) for _, prop := range c.getPropertiesOfType(r.source) { + // Private and protected properties are never produced by keyof, so they have no counterpart in a + // reverse mapped type. Copying them would surface them as public properties of the inferred type. + if getDeclarationModifierFlagsFromSymbol(prop)&ast.ModifierFlagsNonPublicAccessibilityModifier != 0 { + continue + } // In case of a reverse mapped type with an intersection constraint, if we were able to // extract the filtering type literals we skip those properties that are not assignable to them, // because the extra properties wouldn't get through the application of the mapped type anyway @@ -1132,7 +1417,9 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { checkFlags := ast.CheckFlagsReverseMapped | core.IfElse(readonlyMask && c.isReadonlySymbol(prop), ast.CheckFlagsReadonly, 0) inferredProp := c.newSymbolEx(ast.SymbolFlagsProperty|prop.Flags()&optionalMask, prop.Name(), checkFlags) inferredProp.SetDeclarations(prop.Declarations()) - c.valueSymbolLinks.Get(inferredProp).setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) + if nameType := c.valueSymbolLinks.TryGet(prop).getNameType(); nameType != nil { + c.valueSymbolLinks.Get(inferredProp).setNameType(c, nameType) + } links := c.ReverseMappedSymbolLinks.Get(inferredProp) links.propertyType = c.getTypeOfSymbol(prop) constraintTarget := r.constraintType.AsIndexType().target @@ -1148,7 +1435,7 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { links.mappedType = r.mappedType links.constraintType = r.constraintType } - members[prop.Name()] = inferredProp + members.Set(prop.Name(), inferredProp) } c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) } @@ -1238,7 +1525,7 @@ func (c *Checker) isTypeCloselyMatchedBy(s *Type, t *Type) bool { // Create an object with properties named in the string literal type. Every property has type `any`. func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type { - members := make(ast.SymbolTable) + members := c.acquireSymbolTable() for _, t := range t.Distributed() { if t.flags&TypeFlagsStringLiteral == 0 { continue @@ -1250,27 +1537,113 @@ func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type { literalProp.SetDeclarations(t.symbol.Declarations()) literalProp.SetValueDeclaration(t.symbol.ValueDeclaration()) } - members[name] = literalProp + members.Set(name, literalProp) } var indexInfos []*IndexInfo if t.flags&TypeFlagsString != 0 { indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil, nil)} } - return c.newAnonymousType(nil, members, nil, nil, indexInfos) + res := c.newAnonymousType(nil, members, nil, nil, indexInfos) + c.releaseSymbolTableIfUnused(res, members) + return res } func (c *Checker) newInferenceContext(typeParameters []*Type, signature *Signature, flags InferenceFlags, compareTypes TypeComparer) *InferenceContext { + return c.acquireInferenceContext(typeParameters, signature, flags, compareTypes) +} + +func (c *Checker) acquireConditionalInferenceContext(typeParameters []*Type) *InferenceContext { + return c.acquireInferenceContext(typeParameters, nil, InferenceFlagsNone, c.compareTypesAssignable) +} + +func (c *Checker) acquireInferenceContext(typeParameters []*Type, signature *Signature, flags InferenceFlags, compareTypes TypeComparer) *InferenceContext { if compareTypes == nil { compareTypes = c.compareTypesAssignable } - return c.newInferenceContextWorker(core.Map(typeParameters, newInferenceInfo), signature, flags, compareTypes) + var n *InferenceContext + if l := len(c.freeInferenceContexts); l > 0 { + n = c.freeInferenceContexts[l-1] + c.freeInferenceContexts = c.freeInferenceContexts[:l-1] + n.signature = signature + n.flags = flags + n.compareTypes = compareTypes + n.returnMapper = nil + n.outerReturnMapper = nil + n.inferredTypeParameters = nil + n.intraExpressionInferenceSites = nil + n.escaped = false + n.mapper = c.initInferenceTypeMapper(&n.mapperStorage[0], n, true /*fixing*/) + n.nonFixingMapper = c.initInferenceTypeMapper(&n.mapperStorage[1], n, false /*fixing*/) + } else { + n = c.newInferenceContextWorker(nil, signature, flags, compareTypes) + } + inferences := n.inferences[:0] + for _, tp := range typeParameters { + var info *InferenceInfo + if l := len(c.freeInferenceInfos); l > 0 { + info = c.freeInferenceInfos[l-1] + c.freeInferenceInfos = c.freeInferenceInfos[:l-1] + info.typeParameter = tp + info.inferredType = nil + info.priority = InferencePriorityMaxValue + info.topLevel = true + info.isFixed = false + info.impliedArity = -1 + } else { + info = newInferenceInfo(tp) + } + inferences = append(inferences, info) + } + n.inferences = inferences + return n +} + +func (c *Checker) releaseConditionalInferenceContext(n *InferenceContext, fixingCombined *TypeMapper, nonFixingCombined *TypeMapper) { + if fixingCombined != nil && fixingCombined.kind == TypeMapperKindComposite { + c.compositeMapperArena.Pop(castTypeMapper[CompositeTypeMapper](fixingCombined, TypeMapperKindComposite)) + } + if nonFixingCombined != nil && nonFixingCombined.kind == TypeMapperKindComposite { + c.compositeMapperArena.Pop(castTypeMapper[CompositeTypeMapper](nonFixingCombined, TypeMapperKindComposite)) + } + for i, info := range n.inferences { + clear(info.candidates) + info.candidates = info.candidates[:0] + clear(info.contraCandidates) + info.contraCandidates = info.contraCandidates[:0] + info.typeParameter = nil + info.inferredType = nil + c.freeInferenceInfos = append(c.freeInferenceInfos, info) + n.inferences[i] = nil + } + n.inferences = n.inferences[:0] + n.signature = nil + n.returnMapper = nil + n.outerReturnMapper = nil + n.inferredTypeParameters = nil + n.intraExpressionInferenceSites = nil + c.freeInferenceContexts = append(c.freeInferenceContexts, n) } func (c *Checker) cloneInferenceContext(n *InferenceContext, extraFlags InferenceFlags) *InferenceContext { if n == nil { return nil } - return c.newInferenceContextWorker(core.Map(n.inferences, cloneInferenceInfo), n.signature, n.flags|extraFlags, n.compareTypes) + infos := make([]InferenceInfo, len(n.inferences)) + inferences := make([]*InferenceInfo, len(n.inferences)) + for i, info := range n.inferences { + infos[i] = InferenceInfo{ + typeParameter: info.typeParameter, + candidates: slices.Clone(info.candidates), + contraCandidates: slices.Clone(info.contraCandidates), + inferredType: info.inferredType, + priority: info.priority, + topLevel: info.topLevel, + isFixed: info.isFixed, + impliedArity: info.impliedArity, + } + inferences[i] = &infos[i] + } + return c.newInferenceContextWorker(inferences, n.signature, n.flags|extraFlags, n.compareTypes) } func (c *Checker) cloneInferredPartOfContext(n *InferenceContext) *InferenceContext { @@ -1278,7 +1651,22 @@ func (c *Checker) cloneInferredPartOfContext(n *InferenceContext) *InferenceCont if len(inferences) == 0 { return nil } - return c.newInferenceContextWorker(core.Map(inferences, cloneInferenceInfo), n.signature, n.flags, n.compareTypes) + infos := make([]InferenceInfo, len(inferences)) + cloned := make([]*InferenceInfo, len(inferences)) + for i, info := range inferences { + infos[i] = InferenceInfo{ + typeParameter: info.typeParameter, + candidates: slices.Clone(info.candidates), + contraCandidates: slices.Clone(info.contraCandidates), + inferredType: info.inferredType, + priority: info.priority, + topLevel: info.topLevel, + isFixed: info.isFixed, + impliedArity: info.impliedArity, + } + cloned[i] = &infos[i] + } + return c.newInferenceContextWorker(cloned, n.signature, n.flags, n.compareTypes) } func (c *Checker) newInferenceContextWorker(inferences []*InferenceInfo, signature *Signature, flags InferenceFlags, compareTypes TypeComparer) *InferenceContext { @@ -1636,7 +2024,9 @@ func (c *Checker) isSkipDirectInferenceNode(node *ast.Node) bool { } func newInferenceInfo(typeParameter *Type) *InferenceInfo { - return &InferenceInfo{typeParameter: typeParameter, priority: InferencePriorityMaxValue, topLevel: true, impliedArity: -1} + info := &InferenceInfo{typeParameter: typeParameter, priority: InferencePriorityMaxValue, topLevel: true, impliedArity: -1} + info.candidates = info.candidateStorage[:0] + return info } func cloneInferenceInfo(info *InferenceInfo) *InferenceInfo { diff --git a/tsc/internal/checker/jsx.go b/tsc/internal/checker/jsx.go index 7f35f796957cf..705e05d99c470 100644 --- a/tsc/internal/checker/jsx.go +++ b/tsc/internal/checker/jsx.go @@ -285,7 +285,7 @@ func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, cont if s.Name() == jsxChildrenPropertyName && ast.IsJsxElement(element) && len(ast.GetSemanticJsxChildren(element.Children().Nodes)) != 0 { return false } - return node.Symbol().Members()[s.Name()] == nil && c.isDiscriminantProperty(contextualType, s.Name()) + return node.Symbol().Members().Get(s.Name()) == nil && c.isDiscriminantProperty(contextualType, s.Name()) }) discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} discriminated := c.discriminateTypeByDiscriminableItems(contextualType, discriminator) @@ -710,9 +710,9 @@ func (c *Checker) checkApplicableSignatureForJsxCallLikeElement(node *ast.Node, func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeElement *ast.Node, checkMode CheckMode) *Type { var allAttributesTable ast.SymbolTable if c.strictNullChecks { - allAttributesTable = make(ast.SymbolTable) + allAttributesTable = ast.NewSymbolTable() } - attributesTable := make(ast.SymbolTable) + attributesTable := c.acquireSymbolTable() var attributesSymbol *ast.Symbol attributeParent := openingLikeElement spread := c.emptyJsxObjectType @@ -723,6 +723,8 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme createJsxAttributesType := func() *Type { objectFlags |= ObjectFlagsFreshLiteral result := c.newAnonymousType(attributesSymbol, attributesTable, nil, nil, nil) + c.releaseSymbolTableIfUnused(result, attributesTable) + attributesTable = nil result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral return result } @@ -741,18 +743,14 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme if ast.IsJsxAttribute(attributeDecl) { exprType := c.checkJsxAttribute(attributeDecl, checkMode) objectFlags |= exprType.objectFlags & ObjectFlagsPropagatingFlags - attributeSymbol := c.newSymbol(ast.SymbolFlagsProperty|member.Flags(), member.Name()) - attributeSymbol.SetDeclarations(member.Declarations()) - attributeSymbol.SetParent(member.Parent()) - if member.ValueDeclaration() != nil { - attributeSymbol.SetValueDeclaration(member.ValueDeclaration()) - } + attributeSymbol := c.newSharedDataSymbol(member) + attributeSymbol.SetFlags(attributeSymbol.Flags() | ast.SymbolFlagsProperty) links := c.valueSymbolLinks.Get(attributeSymbol) links.resolvedType = exprType links.target = member - attributesTable[attributeSymbol.Name()] = attributeSymbol + attributesTable.Set(attributeSymbol.Name(), attributeSymbol) if allAttributesTable != nil { - allAttributesTable[attributeSymbol.Name()] = attributeSymbol + allAttributesTable.Set(attributeSymbol.Name(), attributeSymbol) } if attributeDecl.Name().Text() == jsxChildrenPropertyName { explicitlySpecifyChildrenAttribute = true @@ -766,9 +764,9 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme } } else { debug.Assert(attributeDecl.Kind == ast.KindJsxSpreadAttribute) - if len(attributesTable) != 0 { + if attributesTable.Len() != 0 { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) - attributesTable = make(ast.SymbolTable) + attributesTable = c.acquireSymbolTable() } exprType := c.getReducedType(c.checkExpressionEx(attributeDecl.Expression(), checkMode&CheckModeInferential)) if IsTypeAny(exprType) { @@ -790,7 +788,7 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme } } if !hasSpreadAnyType { - if len(attributesTable) != 0 { + if attributesTable.Len() != 0 { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) } } @@ -846,15 +844,19 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme childrenPropSymbol.SetValueDeclaration(c.factory.NewPropertySignatureDeclaration(nil, c.factory.NewIdentifier(jsxChildrenPropertyName), nil /*postfixToken*/, nil /*type*/, nil /*initializer*/)) childrenPropSymbol.ValueDeclaration().Parent = attributeParent childrenPropSymbol.ValueDeclaration().AsPropertySignatureDeclaration().Symbol = childrenPropSymbol - childPropMap := make(ast.SymbolTable) - childPropMap[jsxChildrenPropertyName] = childrenPropSymbol - spread = c.getSpreadType(spread, c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil), attributesSymbol, objectFlags|c.getPropagatingFlagsOfTypes(childTypes, TypeFlagsNone), false /*readonly*/) + childPropMap := c.acquireSymbolTable() + childPropMap.Set(jsxChildrenPropertyName, childrenPropSymbol) + childType := c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil) + c.releaseSymbolTableIfUnused(childType, childPropMap) + spread = c.getSpreadType(spread, childType, attributesSymbol, objectFlags|c.getPropagatingFlagsOfTypes(childTypes, TypeFlagsNone), false /*readonly*/) } } if hasSpreadAnyType { + c.releaseSymbolTable(attributesTable) return c.anyType } if typeToIntersect != nil { + c.releaseSymbolTable(attributesTable) if spread != c.emptyJsxObjectType { return c.getIntersectionType([]*Type{typeToIntersect, spread}) } @@ -863,6 +865,7 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme if spread == c.emptyJsxObjectType { return createJsxAttributesType() } + c.releaseSymbolTable(attributesTable) return spread } diff --git a/tsc/internal/checker/links.go b/tsc/internal/checker/links.go index c21981cae9d80..02a3f3d70602f 100644 --- a/tsc/internal/checker/links.go +++ b/tsc/internal/checker/links.go @@ -14,53 +14,149 @@ import ( const maxPageLinkCount = 0x100_0000 // 16M type nodeLinkStore[V any] struct { - gen ast.NodeIdGenerator - links core.LinkStore[uint64, V] - pages core.PagedLinkStore[V] + gen *ast.NodeIdGenerator + localGen ast.NodeIdGenerator + links core.LinkStore[uint64, V] + pages core.PagedLinkStore[V] +} + +func (s *nodeLinkStore[V]) getNodeId(node *ast.Node) uint64 { + if s.gen != nil { + return uint64(s.gen.GetNodeId(node)) + } + return uint64(s.localGen.GetNodeId(node)) } func (s *nodeLinkStore[V]) Get(node *ast.Node) *V { - id := uint64(s.gen.GetNodeId(node)) - if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { - return s.pages.Get(id - ast.BlockIdOffset) + if off := uint64(ast.PeekNodeId(node)) - ast.BlockIdOffset; off < maxPageLinkCount { + if v := s.pages.TryGet(off); v != nil { + return v + } + } + return s.getSlow(node) +} + +func (s *nodeLinkStore[V]) getSlow(node *ast.Node) *V { + id := s.getNodeId(node) + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.Get(off) } return s.links.Get(id) } func (s *nodeLinkStore[V]) Has(node *ast.Node) bool { - return s.TryGet(node) != nil + id := uint64(ast.PeekNodeId(node)) + if id == 0 { + return false + } + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.Has(off) + } + return s.links.Has(id) } func (s *nodeLinkStore[V]) TryGet(node *ast.Node) *V { - id := uint64(s.gen.GetNodeId(node)) - if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { - return s.pages.TryGet(id - ast.BlockIdOffset) + id := uint64(ast.PeekNodeId(node)) + if id == 0 { + return nil + } + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.TryGet(off) + } + return s.links.TryGet(id) +} + +type directNodeLinkStore[V any] struct { + gen *ast.NodeIdGenerator + localGen ast.NodeIdGenerator + links core.LinkStore[uint64, V] + pages core.PagedValueLinkStore[V] +} + +func (s *directNodeLinkStore[V]) getNodeId(node *ast.Node) uint64 { + if s.gen != nil { + return uint64(s.gen.GetNodeId(node)) + } + return uint64(s.localGen.GetNodeId(node)) +} + +func (s *directNodeLinkStore[V]) Get(node *ast.Node) *V { + if off := uint64(ast.PeekNodeId(node)) - ast.BlockIdOffset; off < maxPageLinkCount { + if v := s.pages.TryGet(off); v != nil { + return v + } + } + return s.getSlow(node) +} + +func (s *directNodeLinkStore[V]) getSlow(node *ast.Node) *V { + id := s.getNodeId(node) + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.Get(off) + } + return s.links.Get(id) +} + +func (s *directNodeLinkStore[V]) TryGet(node *ast.Node) *V { + id := uint64(ast.PeekNodeId(node)) + if id == 0 { + return nil + } + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.TryGet(off) } return s.links.TryGet(id) } type symbolLinkStore[V any] struct { - gen ast.SymbolIdGenerator - links core.LinkStore[uint64, V] - pages core.PagedLinkStore[V] + gen *ast.SymbolIdGenerator + localGen ast.SymbolIdGenerator + links core.LinkStore[uint64, V] + pages core.PagedLinkStore[V] +} + +func (s *symbolLinkStore[V]) getSymbolId(symbol *ast.Symbol) uint64 { + if s.gen != nil { + return uint64(s.gen.GetSymbolId(symbol)) + } + return uint64(s.localGen.GetSymbolId(symbol)) } func (s *symbolLinkStore[V]) Get(symbol *ast.Symbol) *V { - id := uint64(s.gen.GetSymbolId(symbol)) - if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { - return s.pages.Get(id - ast.BlockIdOffset) + if off := uint64(ast.PeekSymbolId(symbol)) - ast.BlockIdOffset; off < maxPageLinkCount { + if v := s.pages.TryGet(off); v != nil { + return v + } + } + return s.getSlow(symbol) +} + +func (s *symbolLinkStore[V]) getSlow(symbol *ast.Symbol) *V { + id := s.getSymbolId(symbol) + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.Get(off) } return s.links.Get(id) } func (s *symbolLinkStore[V]) Has(symbol *ast.Symbol) bool { - return s.TryGet(symbol) != nil + id := uint64(ast.PeekSymbolId(symbol)) + if id == 0 { + return false + } + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.Has(off) + } + return s.links.Has(id) } func (s *symbolLinkStore[V]) TryGet(symbol *ast.Symbol) *V { - id := uint64(s.gen.GetSymbolId(symbol)) - if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { - return s.pages.TryGet(id - ast.BlockIdOffset) + id := uint64(ast.PeekSymbolId(symbol)) + if id == 0 { + return nil + } + if off := id - ast.BlockIdOffset; off < maxPageLinkCount { + return s.pages.TryGet(off) } return s.links.TryGet(id) } diff --git a/tsc/internal/checker/mapper.go b/tsc/internal/checker/mapper.go index cb900e7372f89..087b949413b29 100644 --- a/tsc/internal/checker/mapper.go +++ b/tsc/internal/checker/mapper.go @@ -10,9 +10,9 @@ import ( // TypeMapper type TypeMapper struct { - kind TypeMapperKind + kind TypeMapperKind + hasInference bool } - type TypeMapperKind uint8 const ( @@ -50,21 +50,41 @@ var ( func (m *TypeMapper) Map(t *Type) *Type { switch m.kind { case TypeMapperKindSimple: - return castTypeMapper[SimpleTypeMapper](m, TypeMapperKindSimple).Map(t) + s := (*SimpleTypeMapper)(unsafe.Pointer(m)) + if t == s.source { + return s.target + } + return t + case TypeMapperKindArray: + a := (*ArrayTypeMapper)(unsafe.Pointer(m)) + if h := a.hint; h < a.sourcesLen && *(**Type)(unsafe.Add(unsafe.Pointer(a.sourcesPtr), uintptr(h)*unsafe.Sizeof((*Type)(nil)))) == t { + a.hint = h + 1 + return *(**Type)(unsafe.Add(unsafe.Pointer(a.targetsPtr), uintptr(h)*unsafe.Sizeof((*Type)(nil)))) + } + return a.Map(t) + default: + return m.mapSlow(t) + } +} + +func (m *TypeMapper) mapSlow(t *Type) *Type { + switch m.kind { + case TypeMapperKindSimple: + return (*SimpleTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindArray: - return castTypeMapper[ArrayTypeMapper](m, TypeMapperKindArray).Map(t) + return (*ArrayTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindArrayToSingle: - return castTypeMapper[ArrayToSingleTypeMapper](m, TypeMapperKindArrayToSingle).Map(t) + return (*ArrayToSingleTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindDeferred: - return castTypeMapper[DeferredTypeMapper](m, TypeMapperKindDeferred).Map(t) + return (*DeferredTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindFunction: - return castTypeMapper[FunctionTypeMapper](m, TypeMapperKindFunction).Map(t) + return (*FunctionTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindMerged: - return castTypeMapper[MergedTypeMapper](m, TypeMapperKindMerged).Map(t) + return (*MergedTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindComposite: - return castTypeMapper[CompositeTypeMapper](m, TypeMapperKindComposite).Map(t) + return (*CompositeTypeMapper)(unsafe.Pointer(m)).Map(t) case TypeMapperKindInference: - return castTypeMapper[InferenceTypeMapper](m, TypeMapperKindInference).Map(t) + return (*InferenceTypeMapper)(unsafe.Pointer(m)).Map(t) default: panic("Invalid type mapper kind") } @@ -126,10 +146,22 @@ func mergeTypeMappers(m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { } func prependTypeMapping(source *Type, target *Type, mapper *TypeMapper) *TypeMapper { + tp := getNonDistributedTypeParameter(source) if mapper == nil { - return newSimpleTypeMapper(getNonDistributedTypeParameter(source), target) + return newSimpleTypeMapper(tp, target) + } + if mapper.kind == TypeMapperKindSimple { + s := castTypeMapper[SimpleTypeMapper](mapper, TypeMapperKindSimple) + if s.source == tp { + if s.target == target && target != tp { + return mapper + } + if target.flags&TypeFlagsTypeParameter == 0 { + return newSimpleTypeMapper(tp, target) + } + } } - return newMergedTypeMapper(newSimpleTypeMapper(getNonDistributedTypeParameter(source), target), mapper) + return newMergedTypeMapper(newSimpleTypeMapper(tp, target), mapper) } func appendTypeMapping(mapper *TypeMapper, source *Type, target *Type) *TypeMapper { @@ -167,6 +199,13 @@ type SimpleTypeMapper struct { } func newSimpleTypeMapper(source *Type, target *Type) *TypeMapper { + if source != nil && source.checker != nil { + m := source.checker.simpleMapperArena.New() + m.kind = TypeMapperKindSimple + m.source = source + m.target = target + return &m.TypeMapper + } m := &SimpleTypeMapper{} m.kind = TypeMapperKindSimple m.source = source @@ -189,29 +228,80 @@ func (m *SimpleTypeMapper) MapsThisOnly() bool { type ArrayTypeMapper struct { TypeMapperBase - sources []*Type - targets []*Type + sourcesLen uint16 + targetsLen uint16 + hint uint16 + sourcesPtr **Type + targetsPtr **Type } func newArrayTypeMapper(sources []*Type, targets []*Type) *TypeMapper { + var sourcesPtr, targetsPtr **Type + if len(sources) > 0 { + sourcesPtr = unsafe.SliceData(sources) + } + if len(targets) > 0 { + targetsPtr = unsafe.SliceData(targets) + } + if len(sources) > 0 && sources[0] != nil && sources[0].checker != nil { + m := sources[0].checker.arrayMapperArena.New() + m.kind = TypeMapperKindArray + m.sourcesLen = uint16(len(sources)) + m.targetsLen = uint16(len(targets)) + m.sourcesPtr = sourcesPtr + m.targetsPtr = targetsPtr + return &m.TypeMapper + } m := &ArrayTypeMapper{} m.kind = TypeMapperKindArray - m.sources = sources - m.targets = targets + m.sourcesLen = uint16(len(sources)) + m.targetsLen = uint16(len(targets)) + m.sourcesPtr = sourcesPtr + m.targetsPtr = targetsPtr return &m.TypeMapper } +func (m *ArrayTypeMapper) sources() []*Type { + if m.sourcesLen == 0 { + return nil + } + return unsafe.Slice(m.sourcesPtr, m.sourcesLen) +} + +func (m *ArrayTypeMapper) targets() []*Type { + if m.targetsLen == 0 { + return nil + } + return unsafe.Slice(m.targetsPtr, m.targetsLen) +} + func (m *ArrayTypeMapper) Map(t *Type) *Type { - for i, s := range m.sources { - if t == s { - return m.targets[i] + n := int(m.sourcesLen) + if n == 0 { + return t + } + sources := unsafe.Slice(m.sourcesPtr, n) + h := int(m.hint) + if h >= n { + h = 0 + } + for i := h; i < n; i++ { + if sources[i] == t { + m.hint = uint16(i + 1) + return unsafe.Slice(m.targetsPtr, m.targetsLen)[i] + } + } + for i := range h { + if sources[i] == t { + m.hint = uint16(i + 1) + return unsafe.Slice(m.targetsPtr, m.targetsLen)[i] } } return t } func (m *ArrayTypeMapper) MapsThisOnly() bool { - return len(m.sources) == 1 && isThisTypeParameter(m.sources[0]) + return m.sourcesLen == 1 && isThisTypeParameter(*m.sourcesPtr) } // ArrayToSingleTypeMapper @@ -297,8 +387,30 @@ type MergedTypeMapper struct { } func newMergedTypeMapper(m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { + var c *Checker + if m1 != nil && m1.kind == TypeMapperKindSimple { + if s := castTypeMapper[SimpleTypeMapper](m1, TypeMapperKindSimple).source; s != nil { + c = s.checker + } + } else if m2 != nil && m2.kind == TypeMapperKindSimple { + if s := castTypeMapper[SimpleTypeMapper](m2, TypeMapperKindSimple).source; s != nil { + c = s.checker + } + } else if m1 != nil && m1.kind == TypeMapperKindComposite { + c = castTypeMapper[CompositeTypeMapper](m1, TypeMapperKindComposite).c + } + hasInference := m1 != nil && m1.hasInference || m2 != nil && m2.hasInference + if c != nil { + m := c.mergedMapperArena.New() + m.kind = TypeMapperKindMerged + m.hasInference = hasInference + m.m1 = m1 + m.m2 = m2 + return &m.TypeMapper + } m := &MergedTypeMapper{} m.kind = TypeMapperKindMerged + m.hasInference = hasInference m.m1 = m1 m.m2 = m2 return &m.TypeMapper @@ -318,8 +430,19 @@ type CompositeTypeMapper struct { } func newCompositeTypeMapper(c *Checker, m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { + hasInference := m1 != nil && m1.hasInference || m2 != nil && m2.hasInference + if c != nil { + m := c.compositeMapperArena.New() + m.kind = TypeMapperKindComposite + m.hasInference = hasInference + m.c = c + m.m1 = m1 + m.m2 = m2 + return &m.TypeMapper + } m := &CompositeTypeMapper{} m.kind = TypeMapperKindComposite + m.hasInference = hasInference m.c = c m.m1 = m1 m.m2 = m2 @@ -329,6 +452,9 @@ func newCompositeTypeMapper(c *Checker, m1 *TypeMapper, m2 *TypeMapper) *TypeMap func (m *CompositeTypeMapper) Map(t *Type) *Type { t1 := m.m1.Map(t) if t1 != t { + if t1.flags&(TypeFlagsInstantiable|TypeFlagsObject|TypeFlagsUnionOrIntersection) == 0 { + return t1 + } return m.c.instantiateType(t1, m.m2) } return m.m2.Map(t) @@ -338,27 +464,93 @@ func (m *CompositeTypeMapper) Map(t *Type) *Type { type InferenceTypeMapper struct { TypeMapperBase + fixing bool + hint int c *Checker n *InferenceContext - fixing bool } func (c *Checker) initInferenceTypeMapper(m *InferenceTypeMapper, n *InferenceContext, fixing bool) *TypeMapper { m.kind = TypeMapperKindInference + m.hasInference = true + m.fixing = fixing + m.hint = 0 m.c = c m.n = n - m.fixing = fixing return &m.TypeMapper } +func markMapperEscaped(m *TypeMapper) { + if m != nil && m.hasInference { + markMapperEscapedSlow(m) + } +} + +func markMapperEscapedSlow(m *TypeMapper) { + m.hasInference = false + switch m.kind { + case TypeMapperKindInference: + castTypeMapper[InferenceTypeMapper](m, TypeMapperKindInference).n.escaped = true + case TypeMapperKindMerged: + merged := castTypeMapper[MergedTypeMapper](m, TypeMapperKindMerged) + if merged.m1 != nil && merged.m1.hasInference { + markMapperEscapedSlow(merged.m1) + } + if merged.m2 != nil && merged.m2.hasInference { + markMapperEscapedSlow(merged.m2) + } + case TypeMapperKindComposite: + comp := castTypeMapper[CompositeTypeMapper](m, TypeMapperKindComposite) + if comp.m1 != nil && comp.m1.hasInference { + markMapperEscapedSlow(comp.m1) + } + if comp.m2 != nil && comp.m2.hasInference { + markMapperEscapedSlow(comp.m2) + } + } +} + func (m *InferenceTypeMapper) Map(t *Type) *Type { - for i, inference := range m.n.inferences { + inferences := m.n.inferences + n := len(inferences) + if n == 0 { + return t + } + if n == 1 { + inference := inferences[0] + if t == inference.typeParameter { + if m.fixing && !inference.isFixed { + m.c.inferFromIntraExpressionSites(m.n) + clearCachedInferences(inferences) + inference.isFixed = true + } + return m.c.getInferredType(m.n, 0) + } + return t + } + h := m.hint + if h >= n { + h = 0 + } + for i := h; i < n; i++ { + inference := inferences[i] + if t == inference.typeParameter { + m.hint = i + 1 + if m.fixing && !inference.isFixed { + m.c.inferFromIntraExpressionSites(m.n) + clearCachedInferences(inferences) + inference.isFixed = true + } + return m.c.getInferredType(m.n, i) + } + } + for i := range h { + inference := inferences[i] if t == inference.typeParameter { + m.hint = i + 1 if m.fixing && !inference.isFixed { - // Before we commit to a particular inference (and thus lock out any further inferences), - // we infer from any intra-expression inference sites we have collected. m.c.inferFromIntraExpressionSites(m.n) - clearCachedInferences(m.n.inferences) + clearCachedInferences(inferences) inference.isFixed = true } return m.c.getInferredType(m.n, i) diff --git a/tsc/internal/checker/nodebuilder_hover.go b/tsc/internal/checker/nodebuilder_hover.go index 7bf066a03485f..0135a98b4def4 100644 --- a/tsc/internal/checker/nodebuilder_hover.go +++ b/tsc/internal/checker/nodebuilder_hover.go @@ -263,7 +263,7 @@ func (b *NodeBuilderImpl) expandInterfaceDecl(symbol *ast.Symbol) *ast.Node { members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindCallSignature, nil)) } // Properties, filtering inherited - filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.getProperties()) + filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.getPropertiesSlice()) members = b.serializePropertiesWithTruncation(filteredProps, members) // Heritage clauses @@ -413,7 +413,7 @@ func (b *NodeBuilderImpl) serializeNamespaceMember(resolved *ast.Symbol, name st func (b *NodeBuilderImpl) expandModuleDecl(symbol *ast.Symbol) *ast.Node { exports := b.ch.getExportsOfSymbol(symbol) var members []*ast.Symbol - for _, sym := range exports { + for _, sym := range exports.All() { // Filter to namespace-relevant members if !b.isNamespaceMember(sym) { continue @@ -451,7 +451,7 @@ func (b *NodeBuilderImpl) expandModuleDecl(symbol *ast.Symbol) *ast.Node { // Handle alias/re-export symbols if m.Flags()&ast.SymbolFlagsAlias != 0 { aliasDecl := b.ch.getDeclarationOfAliasSymbol(m) - target := b.ch.getMergedSymbol(b.ch.getTargetOfAliasDeclaration(aliasDecl)) + target := b.ch.getMergedSymbol(b.ch.getTargetOfAliasDeclaration(aliasDecl, false /*dontRecursivelyResolve*/)) if target != nil { // If the alias target is a local symbol (not itself an export), emit its declaration first if target.Flags()&(ast.SymbolFlagsBlockScopedVariable|ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsProperty) != 0 { @@ -498,7 +498,7 @@ func (b *NodeBuilderImpl) expandModuleDecl(symbol *ast.Symbol) *ast.Node { } // If the function also has namespace characteristics, emit an empty namespace. merged := b.ch.getMergedSymbol(resolved) - hasModuleExports := merged.Flags()&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 && merged.Exports() != nil && len(merged.Exports()) != 0 + hasModuleExports := merged.Flags()&(ast.SymbolFlagsValueModule|ast.SymbolFlagsNamespaceModule) != 0 && merged.Exports() != nil && merged.Exports().Len() != 0 if !hasModuleExports { bodyStmts = append(bodyStmts, hoverStatement{node: b.f.NewModuleDeclaration(nil, ast.KindNamespaceKeyword, b.f.NewIdentifier(m.Name()), nil /*attributes*/, b.f.NewModuleBlock(b.f.NewNodeList(nil)))}) } diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index 45f8ddc00dcf2..7ccb904cbb2e4 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -773,12 +773,12 @@ func (b *NodeBuilderImpl) createAccessFromSymbolChain(chain []*ast.Symbol, index exports := b.ch.getExportsOfSymbol(parent) if exports != nil { // avoid exhaustive iteration in the common case - res, ok := exports[symbol.Name()] + res, ok := exports.Lookup(symbol.Name()) if symbol.Name() != ast.InternalSymbolNameExportEquals && !isLateBoundName(symbol.Name()) && ok && res != nil && b.ch.getSymbolIfSameReference(res, symbol) != nil { symbolName = symbol.Name() } else { results := make(map[*ast.Symbol]string, 1) - for name, ex := range exports { + for name, ex := range exports.All() { if b.ch.getSymbolIfSameReference(ex, symbol) != nil && !isLateBoundName(name) && name != ast.InternalSymbolNameExportEquals { results[ex] = name // break // must collect all results and sort them - exports are randomly iterated @@ -817,8 +817,8 @@ func (b *NodeBuilderImpl) createAccessFromSymbolChain(chain []*ast.Symbol, index b.ctx.approximateLength += len(symbolName) + 1 if (b.ctx.flags&nodebuilder.FlagsForbidIndexedAccessSymbolReferences == 0) && parent != nil && - b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name()] != nil && - b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name()], symbol) != nil { + b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent).Get(symbol.Name()) != nil && + b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent).Get(symbol.Name()), symbol) != nil { // Should use an indexed access lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) if ast.IsIndexedAccessTypeNode(lhs) { @@ -1112,7 +1112,7 @@ func (b *NodeBuilderImpl) getSymbolChain(symbol *ast.Symbol, meaning ast.SymbolF parentChain := b.getSymbolChain(parent, getQualifiedLeftMeaning(meaning), false, yieldModuleSymbol) if len(parentChain) > 0 { if parent.Exports() != nil { - exported, ok := parent.Exports()[ast.InternalSymbolNameExportEquals] + exported, ok := parent.Exports().Lookup(ast.InternalSymbolNameExportEquals) if ok && b.ch.getSymbolIfSameReference(exported, symbol) != nil { // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent // No need to lookup an alias for the symbol in itself @@ -1897,7 +1897,7 @@ func (b *NodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa // If the expanded parameter list had a variadic in a non-trailing position, don't expand it parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool { return p != expandedParams[len(expandedParams)-1] && p.CheckFlags()&ast.CheckFlagsRestParameter != 0 - }), signature.parameters, expandedParams), func(parameter *ast.Symbol) *ast.Node { + }), b.ch.getSignatureParameters(signature), expandedParams), func(parameter *ast.Symbol) *ast.Node { return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor) }) var thisParameter *ast.Node @@ -1996,9 +1996,10 @@ func (b *NodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa } func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol { + params := c.getSignatureParameters(sig) if signatureHasRestParameter(sig) { - restIndex := len(sig.parameters) - 1 - restSymbol := sig.parameters[restIndex] + restIndex := len(params) - 1 + restSymbol := params[restIndex] restType := c.getTypeOfSymbol(restSymbol) getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string { names := core.MapIndex(t.Target().AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { @@ -2069,7 +2070,7 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) } return symbol }) - return core.Concatenate(sig.parameters[0:restIndex], restParams) + return core.Concatenate(params[0:restIndex], restParams) } if isTupleType(restType) { @@ -2080,12 +2081,12 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) }) } } - return [][]*ast.Symbol{sig.parameters} + return [][]*ast.Symbol{params} } func (b *NodeBuilderImpl) tryGetThisParameterDeclaration(signature *Signature) *ast.Node { if signature.ThisParameter() != nil { - return b.symbolToParameterDeclaration(signature.ThisParameter(), false) + return b.symbolToParameterDeclaration(b.ch.getSignatureThisParameter(signature), false) } if signature.declaration != nil && ast.IsInJSFile(signature.declaration) { // !!! JSDoc Support @@ -2758,7 +2759,7 @@ func (b *NodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *Structur typeElements = slices.Concat(typeElements, b.indexInfoToObjectComputedNamesOrSignatureDeclaration(info, typeNode)) } - properties := resolvedType.getProperties() + properties := resolvedType.getPropertiesSlice() if len(properties) == 0 { return b.f.NewNodeList(typeElements) } @@ -2807,7 +2808,7 @@ func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { resolved := b.ch.resolveStructuredTypeMembers(t) callSigs := resolved.CallSignatures() ctorSigs := resolved.ConstructSignatures() - if len(resolved.getProperties()) == 0 && len(resolved.getIndexInfos()) == 0 { + if len(resolved.getPropertiesSlice()) == 0 && len(resolved.getIndexInfos()) == 0 { if len(callSigs) == 0 && len(ctorSigs) == 0 { b.ctx.approximateLength += 2 result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) @@ -2836,9 +2837,9 @@ func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { return b.ch.getOrCreateTypeFromSignature(s) }) // count the number of type elements excluding abstract constructors - typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.getIndexInfos()) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.getProperties(), func(p *ast.Symbol) bool { + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.getIndexInfos()) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.getPropertiesSlice(), func(p *ast.Symbol) bool { return p.Flags()&ast.SymbolFlagsPrototype == 0 - }), len(resolved.getProperties())) + }), len(resolved.getPropertiesSlice())) // don't include an empty object literal if there were no other static-side // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` // and not `(abstract new () => {}) & {}` diff --git a/tsc/internal/checker/nodebuilderscopes.go b/tsc/internal/checker/nodebuilderscopes.go index 05f033bf05b7d..4d4317a8976b7 100644 --- a/tsc/internal/checker/nodebuilderscopes.go +++ b/tsc/internal/checker/nodebuilderscopes.go @@ -52,7 +52,7 @@ func (b *NodeBuilderImpl) addSymbolTypeToContext(symbol *ast.Symbol, t *Type) fu func (b *NodeBuilderImpl) enterSignatureScope(signature *Signature) (expandedParams []*ast.Symbol, cleanup func()) { expandedParams = b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] - cleanup = b.enterNewScope(signature.declaration, expandedParams, signature.TypeParameters(), signature.parameters, signature.mapper) + cleanup = b.enterNewScope(signature.declaration, expandedParams, signature.TypeParameters(), b.ch.getSignatureParameters(signature), signature.mapper) return expandedParams, cleanup } @@ -119,21 +119,21 @@ func (b *NodeBuilderImpl) enterNewScope(declaration *ast.Node, expandedParams [] locals = existingFakeScope.Locals() } if locals == nil { - locals = make(ast.SymbolTable) + locals = ast.NewSymbolTable() } newLocals := []string{} oldLocals := []localsRecord{} addAll(func(name string, symbol *ast.Symbol) { // Add cleanup information only if we don't own the fake scope if existingFakeScope != nil { - oldSymbol, ok := locals[name] + oldSymbol, ok := locals.Lookup(name) if !ok || oldSymbol == nil { newLocals = append(newLocals, name) } else { oldLocals = append(oldLocals, localsRecord{name, oldSymbol}) } } - locals[name] = symbol + locals.Set(name, symbol) }) if existingFakeScope == nil { @@ -150,10 +150,10 @@ func (b *NodeBuilderImpl) enterNewScope(declaration *ast.Node, expandedParams [] // We did not create the current scope, so we have to clean it up undo := func() { for _, s := range newLocals { - delete(locals, s) + locals.Delete(s) } for _, s := range oldLocals { - locals[s.name] = s.oldSymbol + locals.Set(s.name, s.oldSymbol) } } return undo diff --git a/tsc/internal/checker/pseudotypenodebuilder.go b/tsc/internal/checker/pseudotypenodebuilder.go index 8f1e2bf1700e1..2ec99a8cdecf2 100644 --- a/tsc/internal/checker/pseudotypenodebuilder.go +++ b/tsc/internal/checker/pseudotypenodebuilder.go @@ -178,7 +178,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod d := t.AsPseudoTypeSingleCallSignature() signature := b.ch.getSignatureFromDeclaration(d.Signature) expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] - cleanup := b.enterNewScope(d.Signature, expandedParams, signature.TypeParameters(), signature.parameters, signature.mapper) + cleanup := b.enterNewScope(d.Signature, expandedParams, signature.TypeParameters(), b.ch.getSignatureParameters(signature), signature.mapper) defer cleanup() var typeParams *ast.NodeList if len(d.TypeParameters) > 0 { @@ -233,7 +233,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod if e.Kind != pseudochecker.PseudoObjectElementKindPropertyAssignment { signature := b.ch.getSignatureFromDeclaration(e.Signature()) expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] - cleanup = b.enterNewScope(e.Signature(), expandedParams, signature.TypeParameters(), signature.parameters, signature.mapper) + cleanup = b.enterNewScope(e.Signature(), expandedParams, signature.TypeParameters(), b.ch.getSignatureParameters(signature), signature.mapper) } var newProp *ast.Node switch e.Kind { @@ -589,7 +589,7 @@ func (b *NodeBuilderImpl) pseudoParametersEquivalentToParameters(params []*pseud } return false } else if targetSig.ThisParameter() != nil && ast.IsThisIdentifier(params[0].Name) { - targetParam := targetSig.ThisParameter() + targetParam := b.ch.getSignatureThisParameter(targetSig) paramType := b.ch.getTypeOfParameter(targetParam) if !b.pseudoTypeEquivalentToType(params[0].Type, paramType, params[0].Optional, false) { if reportErrors { @@ -611,7 +611,7 @@ func (b *NodeBuilderImpl) pseudoParametersEquivalentToParameters(params []*pseud return false // TODO: spread tuple params may mess with this check } for i, p := range params { - targetParam := targetSig.parameters[i] + targetParam := b.ch.getSignatureParameters(targetSig)[i] if p.Optional != b.ch.isOptionalParameter(targetParam.ValueDeclaration()) { if reportErrors { b.ctx.tracker.ReportInferenceFallback(p.Name.Parent) diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 007fba2511a13..2d4f64b18b9e5 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -1,9 +1,11 @@ package checker import ( + "math/bits" "slices" "strconv" "strings" + "unsafe" "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/binder" @@ -64,12 +66,18 @@ const ( type RelationComparisonResult uint32 const ( + relationComparisonResultSucceededBitShift = 0 + relationComparisonResultFailedBitShift = 1 + relationComparisonResultReportsUnmeasurableBitShift = 3 + relationComparisonResultReportsUnreliableBitShift = 4 + relationComparisonResultOverflowBitShift = 5 + RelationComparisonResultNone RelationComparisonResult = 0 - RelationComparisonResultSucceeded RelationComparisonResult = 1 << 0 - RelationComparisonResultFailed RelationComparisonResult = 1 << 1 - RelationComparisonResultReportsUnmeasurable RelationComparisonResult = 1 << 3 - RelationComparisonResultReportsUnreliable RelationComparisonResult = 1 << 4 - RelationComparisonResultComplexityOverflow RelationComparisonResult = 1 << 5 + RelationComparisonResultSucceeded RelationComparisonResult = 1 << relationComparisonResultSucceededBitShift + RelationComparisonResultFailed RelationComparisonResult = 1 << relationComparisonResultFailedBitShift + RelationComparisonResultReportsUnmeasurable RelationComparisonResult = 1 << relationComparisonResultReportsUnmeasurableBitShift + RelationComparisonResultReportsUnreliable RelationComparisonResult = 1 << relationComparisonResultReportsUnreliableBitShift + RelationComparisonResultComplexityOverflow RelationComparisonResult = 1 << relationComparisonResultOverflowBitShift RelationComparisonResultReportsMask = RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable RelationComparisonResultOverflow = RelationComparisonResultComplexityOverflow ) @@ -86,32 +94,351 @@ type ErrorOutputContainer struct { type ErrorReporter func(message *diagnostics.Message, args ...any) -type RecursionId struct { - value any -} +type RecursionId uintptr + +const recursionIdIntersection RecursionId = 1 // This function exists to constrain the types of values that can be used as recursion IDs. -func asRecursionId[T *ast.Node | *ast.Symbol | *Type](value T) RecursionId { - return RecursionId{value: value} +func asRecursionId[T *Type | *ast.Symbol | *ast.Node](value T) RecursionId { + return RecursionId(uintptr(unsafe.Pointer(value))) } type Relation struct { - results map[CacheHashKey]RelationComparisonResult + packedResults *packedRelationCache + packedShift uint8 + simpleShift uint8 +} + +const ( + simpleRelationKeyBits = 54 + simpleRelationKeyMask = (uint64(1) << simpleRelationKeyBits) - 1 + simpleRelationSlotMask = 1 | 1<<2 | 1<<4 | 1<<6 | 1<<8 + simpleRelationUpgraded = 0x3ff +) + +type simpleRelationMap struct { + slots []uint64 + count uint32 + shift uint8 +} + +func (m *simpleRelationMap) get(key uint64) uint32 { + if len(m.slots) == 0 { + return 0 + } + i := uint32(((key ^ (key >> 23)) * 0x9e3779b97f4a7c15) >> m.shift) + s := m.slots[i] + if s == 0 { + return 0 + } + if s&simpleRelationKeyMask == key { + return uint32(s >> simpleRelationKeyBits) + } + s = m.slots[(i+1)&uint32(len(m.slots)-1)] + if s == 0 { + return 0 + } + if s&simpleRelationKeyMask == key { + return uint32(s >> simpleRelationKeyBits) + } + return m.getSlow(key, i+2) +} + +//go:noinline +func (m *simpleRelationMap) getSlow(key uint64, i uint32) uint32 { + slots := m.slots + mask := uint32(len(slots) - 1) + for { + s := slots[i&mask] + if s == 0 { + return 0 + } + if s&simpleRelationKeyMask == key { + return uint32(s >> simpleRelationKeyBits) + } + i++ + } +} + +func (m *simpleRelationMap) slot(key uint64) *uint64 { + if len(m.slots) == 0 { + m.slots = make([]uint64, 256) + m.shift = 64 - 8 + } else if int(m.count+1)*4 > len(m.slots)*3 { + m.grow() + } + slots := m.slots + mask := uint32(len(slots) - 1) + i := uint32(((key ^ (key >> 23)) * 0x9e3779b97f4a7c15) >> m.shift) + for { + s := &slots[i&mask] + if *s == 0 { + *s = key + m.count++ + return s + } + if *s&simpleRelationKeyMask == key { + return s + } + i++ + } } -func (r *Relation) get(key CacheHashKey) RelationComparisonResult { - return r.results[key] +func (m *simpleRelationMap) grow() { + old := m.slots + newCap := len(old) * 2 + m.shift-- + if len(old) <= 16384 { + newCap = len(old) * 4 + m.shift-- + } + slots := make([]uint64, newCap) + m.slots = slots + mask := uint32(newCap - 1) + shift := m.shift + for _, s := range old { + if s == 0 { + continue + } + key := s & simpleRelationKeyMask + i := uint32(((key ^ (key >> 23)) * 0x9e3779b97f4a7c15) >> shift) + for slots[i&mask] != 0 { + i++ + } + slots[i&mask] = s + } } -func (r *Relation) set(key CacheHashKey, result RelationComparisonResult) { - if r.results == nil { - r.results = make(map[CacheHashKey]RelationComparisonResult) +type genericRelationSlot struct { + lo0 uint32 + lo1 uint32 + hi0 uint32 + hi1 uint32 + packed uint32 +} + +type genericRelationMap struct { + slots []genericRelationSlot + count uint32 + shift uint8 +} + +func (m *genericRelationMap) get(key CacheHashKey) uint32 { + if len(m.slots) == 0 { + return 0 + } + lo0, lo1 := uint32(key.Lo), uint32(key.Lo>>32) + hi0, hi1 := uint32(key.Hi), uint32(key.Hi>>32) + mask := uint32(len(m.slots) - 1) + i := uint32(key.Lo >> m.shift) + for { + s := m.slots[i] + if s.packed == 0 { + return 0 + } + if s.lo0 == lo0 && s.lo1 == lo1 && s.hi0 == hi0 && s.hi1 == hi1 { + return s.packed + } + i = (i + 1) & mask + } +} + +func (m *genericRelationMap) slot(key CacheHashKey) *genericRelationSlot { + if len(m.slots) == 0 { + m.slots = make([]genericRelationSlot, 32) + m.shift = 64 - 5 + } else if int(m.count+1)*4 > len(m.slots)*3 { + m.grow() + } + lo0, lo1 := uint32(key.Lo), uint32(key.Lo>>32) + hi0, hi1 := uint32(key.Hi), uint32(key.Hi>>32) + mask := uint32(len(m.slots) - 1) + i := uint32(key.Lo >> m.shift) + for { + s := &m.slots[i] + if s.packed == 0 { + s.lo0 = lo0 + s.lo1 = lo1 + s.hi0 = hi0 + s.hi1 = hi1 + m.count++ + return s + } + if s.lo0 == lo0 && s.lo1 == lo1 && s.hi0 == hi0 && s.hi1 == hi1 { + return s + } + i = (i + 1) & mask + } +} + +func (m *genericRelationMap) grow() { + old := m.slots + newCap := len(old) * 2 + m.slots = make([]genericRelationSlot, newCap) + m.shift-- + mask := uint32(newCap - 1) + for _, s := range old { + if s.packed == 0 { + continue + } + lo := uint64(s.lo1)<<32 | uint64(s.lo0) + i := uint32(lo >> m.shift) + for m.slots[i].packed != 0 { + i = (i + 1) & mask + } + m.slots[i] = s + } +} + +type packedRelationCache struct { + genericResults genericRelationMap + simpleResults [4]simpleRelationMap + counts [packedRelationCount]int +} + +type relationKey struct { + value CacheHashKey + generic bool +} + +func newRelations() (subtype *Relation, strictSubtype *Relation, assignable *Relation, comparableRelation *Relation, identity *Relation) { + packedResults := &packedRelationCache{} + identity = newRelation(packedResults, 0, 0) + strictSubtype = newRelation(packedResults, 6, 2) + subtype = newRelation(packedResults, 12, 4) + assignable = newRelation(packedResults, 18, 6) + comparableRelation = newRelation(packedResults, 24, 8) + return subtype, strictSubtype, assignable, comparableRelation, identity +} + +func newRelation(packedResults *packedRelationCache, packedShift uint8, simpleShift uint8) *Relation { + return &Relation{packedResults: packedResults, packedShift: packedShift, simpleShift: simpleShift} +} + +func (r *Relation) get(key relationKey) RelationComparisonResult { + if r.packedResults == nil { + return RelationComparisonResultNone + } + if !key.generic { + s := r.packedResults.simpleResults[key.value.Hi].get(key.value.Lo) + if s == 0 { + return RelationComparisonResultNone + } + if s != simpleRelationUpgraded { + if result := RelationComparisonResult((s >> r.simpleShift) & 3); result != RelationComparisonResultNone { + return result + } + return r.deriveAndSetSimple(key, s) + } + } + s := r.packedResults.genericResults.get(key.value) + if s == 0 { + return RelationComparisonResultNone + } + result := RelationComparisonResult(s>>r.packedShift) & relationComparisonResultMask + if result != RelationComparisonResultNone { + return result + } + return r.deriveAndSetPacked(key, s) +} + +func (r *Relation) deriveAndSetSimple(key relationKey, s uint32) RelationComparisonResult { + laxerSlots := simpleRelationSlotMask &^ (uint32(1)<<(r.simpleShift+2) - 1) + if ((s >> 1) & laxerSlots) != 0 { + return r.set(key, RelationComparisonResultFailed) + } + stricterSlots := simpleRelationSlotMask & (uint32(1)<> relationComparisonResultFailedBitShift) &^ (packed >> relationComparisonResultOverflowBitShift)) & laxerSlots; failedSlots != 0 { + // The lowest set marker is the nearest laxer relation with a usable failure. + shift := bits.TrailingZeros32(failedSlots) + result := RelationComparisonResult(packed>>shift) & relationComparisonResultMask + return r.setPacked(key, packed, result) + } + // A success in a stricter relation implies success in this relation. Align + // each slot's success bit with its respective slot marker, then exclude this + // and all laxer relations. + stricterSlots := packedRelationSlotMask & (uint32(1)<> relationComparisonResultSucceededBitShift) & stricterSlots; succeededSlots != 0 { + // Prefer the strictest available success, matching the previous scan order. + shift := bits.TrailingZeros32(succeededSlots) + result := RelationComparisonResult(packed>>shift) & relationComparisonResultMask + return r.setPacked(key, packed, result) + } + return RelationComparisonResultNone +} + +func (r *Relation) set(key relationKey, result RelationComparisonResult) RelationComparisonResult { + if r.packedResults == nil { + r.packedResults = &packedRelationCache{} + } + result &= relationComparisonResultMask + if !key.generic { + slot := r.packedResults.simpleResults[key.value.Hi].slot(key.value.Lo) + s := uint32(*slot >> simpleRelationKeyBits) + if s != simpleRelationUpgraded { + if uint32(result) <= uint32(RelationComparisonResultFailed) { + if (s>>r.simpleShift)&3 == 0 { + r.packedResults.counts[r.simpleShift>>1]++ + } + shift := simpleRelationKeyBits + r.simpleShift + *slot = (*slot &^ (uint64(3) << shift)) | (uint64(result) << shift) + return result + } + gslot := r.packedResults.genericResults.slot(key.value) + for i := range uint8(packedRelationCount) { + if v := (s >> (i * 2)) & 3; v != 0 { + gslot.packed |= v << (i * packedRelationResultBits) + } + } + *slot = (*slot & simpleRelationKeyMask) | (uint64(simpleRelationUpgraded) << simpleRelationKeyBits) + } } - r.results[key] = result + shiftedResult := uint32(result) << r.packedShift + mask := uint32(relationComparisonResultMask) << r.packedShift + slot := r.packedResults.genericResults.slot(key.value) + if slot.packed&mask == 0 { + r.packedResults.counts[r.packedShift/packedRelationResultBits]++ + } + slot.packed = slot.packed&^mask | shiftedResult + return result +} + +const relationComparisonResultMask = RelationComparisonResultSucceeded | + RelationComparisonResultFailed | + RelationComparisonResultReportsUnmeasurable | + RelationComparisonResultReportsUnreliable | + RelationComparisonResultComplexityOverflow + +// Each map value contains one six-bit result slot per relation, ordered from +// strictest to laxest: identity, strict subtype, subtype, assignable, comparable. +const ( + packedRelationResultBits = 6 + packedRelationCount = 5 + // Marks the low bit of every slot, which is also its success bit. + packedRelationSlotMask = 1 | 1<<6 | 1<<12 | 1<<18 | 1<<24 +) + +func (r *Relation) setPacked(key relationKey, packed uint32, result RelationComparisonResult) RelationComparisonResult { + return r.set(key, result) } func (r *Relation) size() int { - return len(r.results) + if r.packedResults == nil { + return 0 + } + return r.packedResults.counts[r.packedShift/packedRelationResultBits] } func (c *Checker) isTypeIdenticalTo(source *Type, target *Type) bool { @@ -203,6 +530,21 @@ func (c *Checker) isTypeRelatedTo(source *Type, target *Type, relation *Relation } func (c *Checker) isSimpleTypeRelatedTo(source *Type, target *Type, relation *Relation, errorReporter ErrorReporter) bool { + const numberMember = TypeFlagsNumberLiteral | TypeFlagsEnumLiteral + const stringMember = TypeFlagsStringLiteral | TypeFlagsEnumLiteral + s := source.flags + t := target.flags + if s == t && (s == numberMember || s == stringMember) { + return source.AsLiteralType().value == target.AsLiteralType().value && c.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter) + } + if (s|t)&^TypeFlagsStructuredOrInstantiable == 0 { + return t&TypeFlagsUnion != 0 && (relation == c.assignableRelation || relation == c.comparableRelation) && c.isUnknownLikeUnionType(target) + } + return c.isSimpleTypeRelatedToSlow(source, target, relation, errorReporter) +} + +//go:noinline +func (c *Checker) isSimpleTypeRelatedToSlow(source *Type, target *Type, relation *Relation, errorReporter ErrorReporter) bool { s := source.flags t := target.flags if t&TypeFlagsAny != 0 || s&TypeFlagsNever != 0 || source == c.wildcardType { @@ -677,23 +1019,38 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T // A type is 'weak' if it is an object type with at least one optional property // and no required properties, call/construct signatures or index signatures func (c *Checker) isWeakType(t *Type) bool { + if t.cacheFlags&typeCacheIsWeakComputed != 0 { + return t.cacheFlags&typeCacheIsWeak != 0 + } if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.getSignatures()) == 0 && len(resolved.getIndexInfos()) == 0 && len(c.getLayoutPropertiesOfType(t)) > 0 && core.Every(c.getLayoutPropertiesOfType(t), func(p *ast.Symbol) bool { + if len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) != 0 || + len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) != 0 || + len(c.getIndexInfosOfStructuredType(t)) != 0 { + t.cacheFlags |= typeCacheIsWeakComputed + return false + } + hasProperty := false + res := c.everyPropertyOfStructuredType(t, func(p *ast.Symbol) bool { + hasProperty = true return p.Flags()&ast.SymbolFlagsOptional != 0 - }) + }) && hasProperty + t.cacheFlags |= typeCacheIsWeakComputed | core.IfElse(res, typeCacheIsWeak, 0) + return res } if t.flags&TypeFlagsSubstitution != 0 { return c.isWeakType(t.AsSubstitutionType().baseType) } if t.flags&TypeFlagsIntersection != 0 { - return core.Every(t.Types(), c.isWeakType) + res := core.Every(t.Types(), c.isWeakType) + t.cacheFlags |= typeCacheIsWeakComputed | core.IfElse(res, typeCacheIsWeak, 0) + return res } return false } func (c *Checker) hasCommonProperties(source *Type, target *Type, isComparingJsxAttributes bool) bool { - for _, prop := range c.getPropertiesOfType(source) { + props, _ := c.getPropertiesOfTypeLazily(source) + for _, prop := range props { if c.isKnownProperty(target, prop.Name(), isComparingJsxAttributes) { return true } @@ -763,6 +1120,101 @@ func isExcessPropertyCheckTarget(t *Type) bool { // It is possible, though highly unlikely, for the deeply nested check to be true in a situation where a chain of // instantiations is not infinitely expanding. Effectively, we will generate a false positive when two types are // structurally equal to at least maxDepth levels, but unequal at some level beyond that. +type relaterStackEntry struct { + t *Type + target *Type + identity RecursionId + bloom uint64 +} + +func recursionIdBloomMask(id RecursionId) uint64 { + h := uint64(id) * 0x9e3779b97f4a7c15 + return (uint64(1) << (h >> 58)) | (uint64(1) << ((h >> 52) & 63)) +} + +func getIntersectionRecursionBloom(t *Type) uint64 { + var bloom uint64 + for _, elem := range t.Types() { + target := getRecursionIdentityTarget(elem) + if target.flags&TypeFlagsIntersection != 0 { + bloom |= getIntersectionRecursionBloom(target) + } else { + bloom |= recursionIdBloomMask(getRecursionIdentityFromTarget(target)) + } + } + return bloom +} + +func (c *Checker) isDeeplyNestedOnStack(t *Type, stack []relaterStackEntry, maxDepth int) bool { + if len(stack) >= maxDepth { + var target *Type + topIdx := len(stack) - 1 + if stack[topIdx].t == t { + target = stack[topIdx].target + if target == nil { + target = getRecursionIdentityTarget(t) + stack[topIdx].target = target + if target.flags&TypeFlagsIntersection != 0 { + stack[topIdx].identity = recursionIdIntersection + stack[topIdx].bloom = getIntersectionRecursionBloom(target) + } else { + stack[topIdx].identity = getRecursionIdentityFromTarget(target) + } + } + } else { + target = getRecursionIdentityTarget(t) + } + if target.flags&TypeFlagsIntersection != 0 { + for _, t := range target.Types() { + if c.isDeeplyNestedOnStack(t, stack, maxDepth) { + return true + } + } + } else { + var identity RecursionId + if stack[topIdx].t == t { + identity = stack[topIdx].identity + } else { + identity = getRecursionIdentityFromTarget(target) + } + idMask := recursionIdBloomMask(identity) + count := 0 + lastTypeId := TypeId(0) + for i := range stack { + if count+(len(stack)-i) < maxDepth { + return false + } + e := &stack[i] + eid := e.identity + if eid == 0 { + target := getRecursionIdentityTarget(e.t) + e.target = target + if target.flags&TypeFlagsIntersection != 0 { + eid = recursionIdIntersection + e.identity = eid + e.bloom = getIntersectionRecursionBloom(target) + } else { + eid = getRecursionIdentityFromTarget(target) + e.identity = eid + } + } + if eid != identity && (eid != recursionIdIntersection || e.bloom&idMask != idMask) { + continue + } + if e.t.id < lastTypeId || (eid == recursionIdIntersection && !hasMatchingRecursionIdentity(e.target, identity)) { + continue + } + count++ + if count >= maxDepth { + return true + } + lastTypeId = e.t.id + } + } + } + return false +} + func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool { if len(stack) >= maxDepth { target := getRecursionIdentityTarget(t) @@ -776,15 +1228,19 @@ func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool identity := getRecursionIdentityFromTarget(target) count := 0 lastTypeId := TypeId(0) - for _, t := range stack { + for i, t := range stack { + if count+(len(stack)-i) < maxDepth { + return false + } + // We only count occurrences with a higher type id than the previous occurrence, since higher + // type ids are an indicator of newer instantiations caused by recursion. + if t.id < lastTypeId { + continue + } if hasMatchingRecursionIdentity(t, identity) { - // We only count occurrences with a higher type id than the previous occurrence, since higher - // type ids are an indicator of newer instantiations caused by recursion. - if t.id >= lastTypeId { - count++ - if count >= maxDepth { - return true - } + count++ + if count >= maxDepth { + return true } lastTypeId = t.id } @@ -797,14 +1253,23 @@ func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool func hasMatchingRecursionIdentity(t *Type, identity RecursionId) bool { target := getRecursionIdentityTarget(t) if target.flags&TypeFlagsIntersection != 0 { - for _, t := range target.Types() { - if hasMatchingRecursionIdentity(t, identity) { + return hasMatchingRecursionIdentitySlow(target, identity) + } + return getRecursionIdentityFromTarget(target) == identity +} + +func hasMatchingRecursionIdentitySlow(target *Type, identity RecursionId) bool { + for _, t := range target.Types() { + elem := getRecursionIdentityTarget(t) + if elem.flags&TypeFlagsIntersection != 0 { + if hasMatchingRecursionIdentitySlow(elem, identity) { return true } + } else if getRecursionIdentityFromTarget(elem) == identity { + return true } - return false } - return getRecursionIdentityFromTarget(target) == identity + return false } func getRecursionIdentity(t *Type) RecursionId { @@ -818,17 +1283,28 @@ func getRecursionIdentity(t *Type) RecursionId { // each of the mapped type applications will have a unique recursion identity (that of their target object type // literal) and thus avoid appearing deeply nested. func getRecursionIdentityTarget(t *Type) *Type { - if t.flags&TypeFlagsIndexedAccess != 0 { - return getRecursionIdentityTarget(t.AsIndexedAccessType().objectType) + if t.flags&TypeFlagsIndexedAccess == 0 && t.objectFlags&ObjectFlagsInstantiatedMapped != ObjectFlagsInstantiatedMapped { + return t } - if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { - target := t.checker.getModifiersTypeFromMappedType(t) - if target != nil && (target.symbol != nil || target.flags&TypeFlagsIntersection != 0 && - core.Some(target.Types(), func(t *Type) bool { return t.symbol != nil })) { - return getRecursionIdentityTarget(target) + return getRecursionIdentityTargetSlow(t) +} + +func getRecursionIdentityTargetSlow(t *Type) *Type { + for { + if t.flags&TypeFlagsIndexedAccess != 0 { + t = t.AsIndexedAccessType().objectType + continue + } + if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { + target := t.checker.getModifiersTypeFromMappedType(t) + if target != nil && (target.symbol != nil || target.flags&TypeFlagsIntersection != 0 && + core.Some(target.Types(), func(t *Type) bool { return t.symbol != nil })) { + t = target + continue + } } + return t } - return t } // The recursion identity of a type is an object identity that is shared among multiple instantiations of the type. @@ -975,24 +1451,57 @@ func (c *Checker) getUnmatchedProperties(source *Type, target *Type, requireOpti } func (c *Checker) getUnmatchedPropertiesWorker(source *Type, target *Type, requireOptionalProperties bool, matchDiscriminantProperties bool, propsOut *[]*ast.Symbol) *ast.Symbol { - for _, targetProp := range c.getLayoutPropertiesOfType(target) { + properties, lazyType := c.getPropertiesOfTypeLazily(target) + resolvedSource := c.isResolvedPlainObjectType(source) + for _, targetProp := range properties { // TODO: remove this when we support static private identifier fields and find other solutions to get privateNamesAndStaticFields test to pass - if isStaticPrivateIdentifierProperty(targetProp) { - continue - } - if requireOptionalProperties || targetProp.Flags()&ast.SymbolFlagsOptional == 0 && targetProp.CheckFlags()&ast.CheckFlagsPartial == 0 { - sourceProp := c.getPropertyOfType(source, targetProp.Name()) - if sourceProp == nil || matchDiscriminantProperties { - targetProp = c.getPropertyOfType(target, targetProp.Name()) + if (requireOptionalProperties || targetProp.Flags()&ast.SymbolFlagsOptional == 0 && targetProp.CheckFlags()&ast.CheckFlagsPartial == 0) && !isStaticPrivateIdentifierProperty(targetProp) { + if !matchDiscriminantProperties { + var hasProp bool + if resolvedSource { + hasProp = c.getResolvedPropertyOfPlainObjectType(source, targetProp.Name()) != nil + } else { + hasProp = c.hasPropertyOfType(source, targetProp.Name()) + } + if !hasProp { + if lazyType != nil { + targetProp = c.getPropertyOfType(lazyType, targetProp.Name()) + } + if propsOut == nil { + return targetProp + } + *propsOut = append(*propsOut, targetProp) + } + continue + } + var sourceProp *ast.Symbol + if resolvedSource { + sourceProp = c.getResolvedPropertyOfPlainObjectType(source, targetProp.Name()) + } else { + sourceProp = c.getPropertyOfTypeWorker(source, targetProp.Name(), false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/, false /*instantiate*/) } if sourceProp == nil { + if lazyType != nil { + targetProp = c.getPropertyOfType(lazyType, targetProp.Name()) + } if propsOut == nil { return targetProp } *propsOut = append(*propsOut, targetProp) - } else if matchDiscriminantProperties { + } else if targetProp.Flags()&ast.SymbolFlagsMethod == 0 || targetProp.Flags()&(ast.SymbolFlagsProperty|ast.SymbolFlagsVariable|ast.SymbolFlagsGetAccessor|ast.SymbolFlagsEnumMember) != 0 { + if lazyType != nil { + if lazyTarget := lazyType; lazyTarget.objectFlags&ObjectFlagsMapped == 0 { + if links := c.valueSymbolLinks.TryGet(targetProp); links != nil && links.resolvedType != nil && links.resolvedType.flags&TypeFlagsUnit == 0 && !c.couldContainTypeVariables(links.resolvedType) { + continue + } + } + targetProp = c.getPropertyOfType(lazyType, targetProp.Name()) + } targetType := c.getTypeOfSymbol(targetProp) if targetType.flags&TypeFlagsUnit != 0 { + if !resolvedSource { + sourceProp = c.getPropertyOfType(source, targetProp.Name()) + } sourceType := c.getTypeOfSymbol(sourceProp) if !(sourceType.flags&TypeFlagsAny != 0 || c.getRegularTypeOfLiteralType(sourceType) == c.getRegularTypeOfLiteralType(targetType)) { if propsOut == nil { @@ -1107,24 +1616,38 @@ func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) * return c.getConstituentTypeForKeyType(unionType, propType) } +type unionDiscriminant struct { + keyPropertyName string + constituentMap map[*Type]*Type +} + // Return the name of a discriminant property for which it was possible and feasible to construct a map of // constituent types keyed by the literal types of the property by that name in each constituent type. Return // an empty string if no such discriminant property exists. func (c *Checker) getKeyPropertyName(t *Type) string { + types := t.Types() + if len(types) < 10 || t.objectFlags&ObjectFlagsPrimitiveUnion != 0 { + return "" + } u := t.AsUnionType() - if u.keyPropertyName == "" { - u.keyPropertyName, u.constituentMap = c.computeKeyPropertyNameAndMap(t) + disc, ok := c.unionDiscriminants[u] + if !ok { + disc.keyPropertyName, disc.constituentMap = c.computeKeyPropertyNameAndMap(t) + if c.unionDiscriminants == nil { + c.unionDiscriminants = make(map[*UnionType]unionDiscriminant) + } + c.unionDiscriminants[u] = disc } - if u.keyPropertyName == ast.InternalSymbolNameMissing { + if disc.keyPropertyName == ast.InternalSymbolNameMissing { return "" } - return u.keyPropertyName + return disc.keyPropertyName } // Given a union type for which getKeyPropertyName returned a non-empty string, return the constituent // that corresponds to the given key type for that property name. func (c *Checker) getConstituentTypeForKeyType(t *Type, keyType *Type) *Type { - result := t.AsUnionType().constituentMap[c.getRegularTypeOfLiteralType(keyType)] + result := c.unionDiscriminants[t.AsUnionType()].constituentMap[c.getRegularTypeOfLiteralType(keyType)] if result != c.unknownType { return result } @@ -1487,10 +2010,17 @@ func (c *Checker) hasCovariantVoidArgument(typeArguments []*Type, variances []Va } func (c *Checker) isSignatureAssignableTo(source *Signature, target *Signature, ignoreReturnTypes bool) bool { - return c.compareSignaturesRelated(source, target, core.IfElse(ignoreReturnTypes, SignatureCheckModeIgnoreReturnTypes, SignatureCheckModeNone), false /*reportErrors*/, nil /*errorReporter*/, c.compareTypesAssignable, nil /*reportUnreliableMarkers*/) != TernaryFalse + return c.compareSignaturesRelated(source, target, core.IfElse(ignoreReturnTypes, SignatureCheckModeIgnoreReturnTypes, SignatureCheckModeNone), false /*reportErrors*/, nil /*r*/, IntersectionStateNone) != TernaryFalse +} + +func (c *Checker) compareTypesInSignature(r *Relater, intersectionState IntersectionState, source *Type, target *Type, reportErrors bool) Ternary { + if r != nil { + return r.isRelatedToEx(source, target, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) + } + return c.compareTypesAssignableWorker(source, target, reportErrors) } -func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, checkMode SignatureCheckMode, reportErrors bool, errorReporter ErrorReporter, compareTypes TypeComparer, reportUnreliableMarkers *TypeMapper) Ternary { +func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, checkMode SignatureCheckMode, reportErrors bool, r *Relater, intersectionState IntersectionState) Ternary { if source == target { return TernaryTrue } @@ -1513,18 +2043,32 @@ func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, if reportErrors && (checkMode&SignatureCheckModeStrictArity == 0) { // the second condition should be redundant, because there is no error reporting when comparing signatures by strict arity // since it is only done for subtype reduction - errorReporter(diagnostics.Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1, c.getMinArgumentCount(source), targetCount) + r.reportError(diagnostics.Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1, c.getMinArgumentCount(source), targetCount) } return TernaryFalse } if len(source.TypeParameters()) != 0 && !core.Same(source.TypeParameters(), target.TypeParameters()) { target = c.getCanonicalSignature(target) - source = c.instantiateSignatureInContextOf(source, target /*inferenceContext*/, nil, compareTypes) + var compareTypes TypeComparer + if r == nil { + compareTypes = c.compareTypesAssignable + } else if intersectionState == IntersectionStateNone { + compareTypes = r.compareTypes + } else { + compareTypes = func(source *Type, target *Type, reportErrors bool) Ternary { + return r.isRelatedToEx(source, target, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) + } + } + source = c.instantiateSignatureInContextOf(source, target, nil /*inferenceContext*/, compareTypes) } sourceCount := c.getParameterCount(source) sourceRestType := c.getNonArrayRestType(source) targetRestType := c.getNonArrayRestType(target) if sourceRestType != nil || targetRestType != nil { + var reportUnreliableMarkers *TypeMapper + if r != nil { + reportUnreliableMarkers = r.c.reportUnreliableMapper + } c.instantiateType(core.IfElse(sourceRestType != nil, sourceRestType, targetRestType), reportUnreliableMarkers) } kind := ast.KindUnknown @@ -1537,17 +2081,17 @@ func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, if sourceThisType != nil && sourceThisType != c.voidType { targetThisType := c.getThisTypeOfSignature(target) if targetThisType != nil { - // void sources are assignable to anything. + // Void sources are assignable to anything. var related Ternary if !strictVariance { - related = compareTypes(sourceThisType, targetThisType, false /*reportErrors*/) + related = c.compareTypesInSignature(r, intersectionState, sourceThisType, targetThisType, false /*reportErrors*/) } if related == TernaryFalse { - related = compareTypes(targetThisType, sourceThisType, reportErrors) + related = c.compareTypesInSignature(r, intersectionState, targetThisType, sourceThisType, reportErrors) } if related == TernaryFalse { if reportErrors { - errorReporter(diagnostics.The_this_types_of_each_signature_are_incompatible) + r.reportError(diagnostics.The_this_types_of_each_signature_are_incompatible) } return TernaryFalse } @@ -1585,37 +2129,42 @@ func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, // they naturally relate only contra-variantly). However, if the source and target parameters both have // function types with a single call signature, we know we are relating two callback parameters. In // that case it is sufficient to only relate the parameters of the signatures co-variantly because, - // similar to return values, callback parameters are output positions. This means that a Promise, + // similar to return values, callback parameters are output positions. This means that a Promise // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) // with respect to T. var sourceSig *Signature var targetSig *Signature - if checkMode&SignatureCheckModeCallback == 0 && !c.isInstantiatedGenericParameter(source, i) { + if checkMode&SignatureCheckModeCallback == 0 { sourceSig = c.getSingleCallSignature(c.GetNonNullableType(sourceType)) - } - if checkMode&SignatureCheckModeCallback == 0 && !c.isInstantiatedGenericParameter(target, i) { - targetSig = c.getSingleCallSignature(c.GetNonNullableType(targetType)) + if sourceSig != nil && !c.isInstantiatedGenericParameter(source, i) { + targetSig = c.getSingleCallSignature(c.GetNonNullableType(targetType)) + if targetSig != nil && c.isInstantiatedGenericParameter(target, i) { + targetSig = nil + } + } else { + sourceSig = nil + } } callbacks := sourceSig != nil && targetSig != nil && c.getTypePredicateOfSignature(sourceSig) == nil && c.getTypePredicateOfSignature(targetSig) == nil && c.getTypeFacts(sourceType, TypeFactsIsUndefinedOrNull) == c.getTypeFacts(targetType, TypeFactsIsUndefinedOrNull) var related Ternary if callbacks { - related = c.compareSignaturesRelated(targetSig, sourceSig, checkMode&SignatureCheckModeStrictArity|core.IfElse(strictVariance, SignatureCheckModeStrictCallback, SignatureCheckModeBivariantCallback), reportErrors, errorReporter, compareTypes, reportUnreliableMarkers) + related = c.compareSignaturesRelated(targetSig, sourceSig, checkMode&SignatureCheckModeStrictArity|core.IfElse(strictVariance, SignatureCheckModeStrictCallback, SignatureCheckModeBivariantCallback), reportErrors, r, intersectionState) } else { if checkMode&SignatureCheckModeCallback == 0 && !strictVariance { - related = compareTypes(sourceType, targetType, false /*reportErrors*/) + related = c.compareTypesInSignature(r, intersectionState, sourceType, targetType, false /*reportErrors*/) } if related == TernaryFalse { - related = compareTypes(targetType, sourceType, reportErrors) + related = c.compareTypesInSignature(r, intersectionState, targetType, sourceType, reportErrors) } } // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void - if related != TernaryFalse && checkMode&SignatureCheckModeStrictArity != 0 && i >= c.getMinArgumentCount(source) && i < c.getMinArgumentCount(target) && compareTypes(sourceType, targetType, false /*reportErrors*/) != TernaryFalse { + if related != TernaryFalse && checkMode&SignatureCheckModeStrictArity != 0 && i >= c.getMinArgumentCount(source) && i < c.getMinArgumentCount(target) && c.compareTypesInSignature(r, intersectionState, sourceType, targetType, false /*reportErrors*/) != TernaryFalse { related = TernaryFalse } if related == TernaryFalse { if reportErrors { - errorReporter(diagnostics.Types_of_parameters_0_and_1_are_incompatible, c.getParameterNameAtPosition(source, i), c.getParameterNameAtPosition(target, i)) + r.reportError(diagnostics.Types_of_parameters_0_and_1_are_incompatible, c.getParameterNameAtPosition(source, i), c.getParameterNameAtPosition(target, i)) } return TernaryFalse } @@ -1635,58 +2184,57 @@ func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, if targetTypePredicate != nil { sourceTypePredicate := c.getTypePredicateOfSignature(source) if sourceTypePredicate != nil { - result &= c.compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes) + result &= c.compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, r, intersectionState) } else if targetTypePredicate.kind == TypePredicateKindIdentifier || targetTypePredicate.kind == TypePredicateKindThis { if reportErrors { - errorReporter(diagnostics.Signature_0_must_be_a_type_predicate, c.signatureToString(source)) + r.reportError(diagnostics.Signature_0_must_be_a_type_predicate, c.signatureToString(source)) } return TernaryFalse } - } else { - // When relating callback signatures, we still need to relate return types bi-variantly as otherwise - // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } - // wouldn't be co-variant for T without this rule. - var related Ternary - if checkMode&SignatureCheckModeBivariantCallback != 0 { - related = compareTypes(targetReturnType, sourceReturnType, false /*reportErrors*/) - } - if related == TernaryFalse { - related = compareTypes(sourceReturnType, targetReturnType, reportErrors) - } - result &= related - if result == TernaryFalse && reportErrors { - // The errors reported here serve as markers that trigger error chain reduction in the (*Relater).reportError - // method. The markers are elided in the final diagnostic chain and never actually reported. - var message *diagnostics.Message - if len(source.parameters) == 0 && len(target.parameters) == 0 { - message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, - diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, - diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1) - } else { - message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, - diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, - diagnostics.Call_signature_return_types_0_and_1_are_incompatible) - } - errorReporter(message, c.TypeToString(sourceReturnType), c.TypeToString(targetReturnType)) + } + // When relating callback signatures, we still need to relate return types bi-variantly as otherwise + // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } + // wouldn't be co-variant for T without this rule. + var related Ternary + if checkMode&SignatureCheckModeBivariantCallback != 0 { + related = c.compareTypesInSignature(r, intersectionState, targetReturnType, sourceReturnType, false /*reportErrors*/) + } + if related == TernaryFalse { + related = c.compareTypesInSignature(r, intersectionState, sourceReturnType, targetReturnType, reportErrors) + } + result &= related + if result == TernaryFalse && reportErrors { + // The errors reported here serve as markers that trigger error chain reduction in the (*Relater).reportError + // method. The markers are elided in the final diagnostic chain and never actually reported. + var message *diagnostics.Message + if len(source.parameters) == 0 && len(target.parameters) == 0 { + message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, + diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, + diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1) + } else { + message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, + diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, + diagnostics.Call_signature_return_types_0_and_1_are_incompatible) } + r.reportError(message, c.TypeToString(sourceReturnType), c.TypeToString(targetReturnType)) } } return result } -func (c *Checker) compareTypePredicateRelatedTo(source *TypePredicate, target *TypePredicate, reportErrors bool, errorReporter ErrorReporter, compareTypes TypeComparer) Ternary { +func (c *Checker) compareTypePredicateRelatedTo(source *TypePredicate, target *TypePredicate, reportErrors bool, r *Relater, intersectionState IntersectionState) Ternary { if source.kind != target.kind { if reportErrors { - errorReporter(diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard) - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) + r.reportError(diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard) + r.reportError(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) } return TernaryFalse } if source.kind == TypePredicateKindIdentifier || source.kind == TypePredicateKindAssertsIdentifier { if source.parameterIndex != target.parameterIndex { if reportErrors { - errorReporter(diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, source.parameterName, target.parameterName) - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) + r.reportError(diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, source.parameterName, target.parameterName) + r.reportError(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) } return TernaryFalse } @@ -1696,20 +2244,20 @@ func (c *Checker) compareTypePredicateRelatedTo(source *TypePredicate, target *T case source.t == target.t: related = TernaryTrue case source.t != nil && target.t != nil: - related = compareTypes(source.t, target.t, reportErrors) + related = c.compareTypesInSignature(r, intersectionState, source.t, target.t, reportErrors) default: related = TernaryFalse } if related == TernaryFalse && reportErrors { - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) + r.reportError(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) } return related } // Returns true if `s` is `(...args: A) => R` where `A` is `any`, `any[]`, `never`, or `never[]`, and `R` is `any` or `unknown`. func (c *Checker) isTopSignature(s *Signature) bool { - if len(s.TypeParameters()) == 0 && (s.ThisParameter() == nil || IsTypeAny(c.getTypeOfParameter(s.ThisParameter()))) && len(s.parameters) == 1 && signatureHasRestParameter(s) { - paramType := c.getTypeOfParameter(s.parameters[0]) + if len(s.TypeParameters()) == 0 && (s.ThisParameter() == nil || IsTypeAny(c.getTypeOfParameter(c.getSignatureThisParameter(s)))) && len(s.parameters) == 1 && signatureHasRestParameter(s) { + paramType := c.getTypeOfSignatureParameter(s, 0) var restType *Type if c.isArrayType(paramType) { restType = c.getTypeArguments(paramType)[0] @@ -1728,7 +2276,7 @@ func (c *Checker) isTopSignature(s *Signature) bool { func (c *Checker) getParameterCount(signature *Signature) int { length := len(signature.parameters) if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[length-1]) + restType := c.getTypeOfSignatureParameterSymbol(signature, length-1) if isTupleType(restType) { return length + restType.TargetTupleType().fixedLength - core.IfElse(restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0, 0, 1) } @@ -1746,7 +2294,7 @@ func (c *Checker) getMinArgumentCountEx(signature *Signature, flags MinArgumentC if voidIsNonOptional != 0 || signature.resolvedMinArgumentCount == -1 { minArgumentCount := -1 if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) + restType := c.getTypeOfSignatureParameterSymbol(signature, len(signature.parameters)-1) if isTupleType(restType) { firstOptionalIndex := core.FindIndex(restType.TargetTupleType().elementInfos, func(info TupleElementInfo) bool { return info.flags&ElementFlagsRequired == 0 @@ -1783,7 +2331,7 @@ func (c *Checker) getMinArgumentCountEx(signature *Signature, flags MinArgumentC func (c *Checker) hasEffectiveRestParameter(signature *Signature) bool { if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) + restType := c.getTypeOfSignatureParameterSymbol(signature, len(signature.parameters)-1) return !isTupleType(restType) || restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 } return false @@ -1800,13 +2348,13 @@ func (c *Checker) getTypeAtPosition(signature *Signature, pos int) *Type { func (c *Checker) tryGetTypeAtPosition(signature *Signature, pos int) *Type { paramCount := len(signature.parameters) - core.IfElse(signatureHasRestParameter(signature), 1, 0) if pos < paramCount { - return c.getTypeOfParameter(signature.parameters[pos]) + return c.getTypeOfSignatureParameter(signature, pos) } if signatureHasRestParameter(signature) { // We want to return the value undefined for an out of bounds parameter position, // so we need to check bounds here before calling getIndexedAccessType (which // otherwise would return the type 'undefined'). - restType := c.getTypeOfSymbol(signature.parameters[paramCount]) + restType := c.getTypeOfSignatureParameterSymbol(signature, paramCount) index := pos - paramCount if !isTupleType(restType) || restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 || index < restType.TargetTupleType().fixedLength { return c.getIndexedAccessType(restType, c.getNumberLiteralType(jsnum.Number(index))) @@ -1870,7 +2418,7 @@ func (c *Checker) getNameableDeclarationAtPosition(signature *Signature, pos int } if signatureHasRestParameter(signature) { restParameter := signature.parameters[paramCount] - restType := c.getTypeOfSymbol(restParameter) + restType := c.getTypeOfSignatureParameterSymbol(signature, paramCount) if isTupleType(restType) { elementInfos := restType.TargetTupleType().elementInfos index := pos - paramCount @@ -1900,7 +2448,7 @@ func (c *Checker) getNonArrayRestType(signature *Signature) *Type { func (c *Checker) getEffectiveRestType(signature *Signature) *Type { if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) + restType := c.getTypeOfSignatureParameterSymbol(signature, len(signature.parameters)-1) if !isTupleType(restType) { if IsTypeAny(restType) { return c.anyArrayType @@ -1948,17 +2496,36 @@ func (c *Checker) getRestArrayTypeOfTupleType(t *Type) *Type { func (c *Checker) getThisTypeOfSignature(signature *Signature) *Type { if signature.ThisParameter() != nil { + if signature.flags&signatureFlagsUninstantiatedThisParameter != 0 { + target := signature.target + mapper := signature.mapper + for target.flags&signatureFlagsUninstantiatedThisParameter != 0 { + mapper = c.combineTypeMappers(target.mapper, mapper) + target = target.target + } + thisParam := target.ThisParameter() + if thisParam.CheckFlags()&ast.CheckFlagsInstantiated != 0 { + links := c.valueSymbolLinks.Get(thisParam) + thisParam = links.target + mapper = c.combineTypeMappers(links.mapper, mapper) + } + t := c.getTypeOfSymbol(thisParam) + if c.couldContainTypeVariables(t) || thisParam.Flags()&ast.SymbolFlagsSetAccessor != 0 { + t = c.instantiateType(t, mapper) + } + return t + } return c.getTypeOfSymbol(signature.ThisParameter()) } return nil } func (c *Checker) isInstantiatedGenericParameter(signature *Signature, pos int) bool { - if signature.target == nil { - return false + if signature.target != nil { + t := c.tryGetTypeAtPosition(signature.target, pos) + return t != nil && c.isGenericType(t) } - t := c.tryGetTypeAtPosition(signature.target, pos) - return t != nil && c.isGenericType(t) + return false } func (c *Checker) getParameterNameAtPosition(signature *Signature, pos int) string { @@ -1967,7 +2534,7 @@ func (c *Checker) getParameterNameAtPosition(signature *Signature, pos int) stri return signature.parameters[pos].Name() } restParameter := signature.parameters[paramCount] - restType := c.getTypeOfSymbol(restParameter) + restType := c.getTypeOfSignatureParameterSymbol(signature, paramCount) if isTupleType(restType) { index := pos - paramCount return c.getTupleElementLabel(restType.TargetTupleType().elementInfos[index], restParameter, index) @@ -2316,6 +2883,7 @@ func (c *Checker) compareTypePredicatesIdentical(source *TypePredicate, target * } func (c *Checker) getEffectiveConstraintOfIntersection(types []*Type, targetIsUnion bool) *Type { + var constraintsBuf [8]*Type var constraints []*Type hasDisjointDomainType := false for _, t := range types { @@ -2327,6 +2895,9 @@ func (c *Checker) getEffectiveConstraintOfIntersection(types []*Type, targetIsUn constraint = c.getConstraintOfType(constraint) } if constraint != nil { + if constraints == nil { + constraints = constraintsBuf[:0] + } constraints = append(constraints, constraint) if targetIsUnion { constraints = append(constraints, t) @@ -2368,6 +2939,7 @@ func (c *Checker) templateLiteralTypesDefinitelyUnrelated(source *TemplateLitera func (c *Checker) isTypeMatchedByTemplateLiteralType(source *Type, target *TemplateLiteralType, compareTypes TypeComparer) bool { inferences := c.inferTypesFromTemplateLiteralType(source, target, compareTypes) if inferences != nil { + defer c.releaseTemporaryTypeArguments(inferences) for i, inference := range inferences { if !c.isValidTypeForTemplateLiteralPlaceholder(inference, target.types[i], compareTypes) { return false @@ -2381,15 +2953,21 @@ func (c *Checker) isTypeMatchedByTemplateLiteralType(source *Type, target *Templ func (c *Checker) inferTypesFromTemplateLiteralType(source *Type, target *TemplateLiteralType, compareTypes TypeComparer) []*Type { switch { case source.flags&TypeFlagsStringLiteral != 0: - return c.inferFromLiteralPartsToTemplateLiteral([]string{getStringLiteralValue(source)}, nil, target) + var sourceTexts [1]string + sourceTexts[0] = getStringLiteralValue(source) + return c.inferFromLiteralPartsToTemplateLiteral(sourceTexts[:], nil, target) case source.flags&TypeFlagsTemplateLiteral != 0: if slices.Equal(source.AsTemplateLiteralType().texts, target.texts) { - return core.MapIndex(source.AsTemplateLiteralType().types, func(s *Type, i int) *Type { + sourceTypes := source.AsTemplateLiteralType().types + matches := c.getTemporaryTypeArguments(len(sourceTypes)) + for i, s := range sourceTypes { if compareTypes(c.getBaseConstraintOrType(s), c.getBaseConstraintOrType(target.types[i]), false /*partialMatch*/) != TernaryFalse { - return s + matches[i] = s + } else { + matches[i] = c.getStringLikeTypeForType(s) } - return c.getStringLikeTypeForType(s) - }) + } + return matches } return c.inferFromLiteralPartsToTemplateLiteral(source.AsTemplateLiteralType().texts, source.AsTemplateLiteralType().types, target) default: @@ -2432,7 +3010,7 @@ func (c *Checker) inferFromLiteralPartsToTemplateLiteral(sourceTexts []string, s remainingEndText := sourceEndText[:len(sourceEndText)-len(targetEndText)] seg := 0 pos := len(targetStartText) - var matches []*Type + matches := c.getTemporaryTypeArguments(lastTargetIndex)[:0] getSourceText := func(index int) string { if index < lastSourceIndex { return sourceTexts[index] @@ -2444,7 +3022,13 @@ func (c *Checker) inferFromLiteralPartsToTemplateLiteral(sourceTexts []string, s if s == seg { matchType = c.getStringLiteralType(stringutil.CombineSurrogatePairs(getSourceText(s)[pos:p])) } else { - matchTexts := make([]string, s-seg+1) + var matchTextsBuf [8]string + var matchTexts []string + if s-seg+1 <= len(matchTextsBuf) { + matchTexts = matchTextsBuf[:s-seg+1] + } else { + matchTexts = make([]string, s-seg+1) + } matchTexts[0] = sourceTexts[seg][pos:] copy(matchTexts[1:], sourceTexts[seg+1:s]) matchTexts[s-seg] = getSourceText(s)[:p] @@ -2467,6 +3051,7 @@ func (c *Checker) inferFromLiteralPartsToTemplateLiteral(sourceTexts []string, s } s++ if s == len(sourceTexts) { + c.releaseTemporaryTypeArguments(matches) return nil } p = 0 @@ -2495,6 +3080,7 @@ func (c *Checker) inferFromLiteralPartsToTemplateLiteral(sourceTexts []string, s } else if seg < lastSourceIndex { addMatch(seg+1, 0) } else { + c.releaseTemporaryTypeArguments(matches) return nil } } @@ -2586,16 +3172,17 @@ type Relater struct { errorNode *ast.Node errorChain *ErrorChain relatedInfo []*ast.Diagnostic - maybeKeys []CacheHashKey - maybeKeysSet collections.Set[CacheHashKey] - sourceStack []*Type - targetStack []*Type + maybeKeys []relationKey + maybeKeysSet collections.Set[relationKey] + sourceStack []relaterStackEntry + targetStack []relaterStackEntry maybeCount int sourceDepth int targetDepth int expandingFlags ExpandingFlags overflow bool relationCount int + compareTypes TypeComparer next *Relater } @@ -2603,19 +3190,44 @@ func (c *Checker) getRelater() *Relater { r := c.freeRelater if r == nil { r = &Relater{c: c} + r.compareTypes = r.isRelatedToWorker } c.freeRelater = r.next return r } +const maybeKeysSetThreshold = 32 + +func (r *Relater) hasMaybeKey(key relationKey) bool { + if len(r.maybeKeys) > maybeKeysSetThreshold { + return r.maybeKeysSet.Has(key) + } + return slices.Contains(r.maybeKeys, key) +} + +func (r *Relater) pushMaybeKey(key relationKey) { + r.maybeKeys = append(r.maybeKeys, key) + if len(r.maybeKeys) > maybeKeysSetThreshold { + if len(r.maybeKeys) == maybeKeysSetThreshold+1 { + for _, k := range r.maybeKeys[:maybeKeysSetThreshold] { + r.maybeKeysSet.Add(k) + } + } + r.maybeKeysSet.Add(key) + } +} + func (c *Checker) putRelater(r *Relater) { - r.maybeKeysSet.Clear() + if len(r.maybeKeys) > maybeKeysSetThreshold { + r.maybeKeysSet.Clear() + } *r = Relater{ c: c, maybeKeys: r.maybeKeys[:0], maybeKeysSet: r.maybeKeysSet, sourceStack: r.sourceStack[:0], targetStack: r.targetStack[:0], + compareTypes: r.compareTypes, next: c.freeRelater, } c.freeRelater = r @@ -2869,8 +3481,39 @@ func (c *Checker) isTypeSubsetOf(source *Type, target *Type) bool { func (c *Checker) isTypeSubsetOfUnion(source *Type, target *Type) bool { if source.flags&TypeFlagsUnion != 0 { - for _, t := range source.Types() { - if !containsType(target.Types(), t) { + sourceTypes := source.Types() + targetTypes := target.Types() + if len(sourceTypes) > len(targetTypes) { + return false + } + if len(c.files) > 0 { + if len(sourceTypes)*bits.Len(uint(len(targetTypes)))*8 >= len(targetTypes) { + for i, t := range sourceTypes { + if len(targetTypes) < len(sourceTypes)-i { + return false + } + k := slices.Index(targetTypes, t) + if k < 0 { + return false + } + targetTypes = targetTypes[k+1:] + } + return true + } + for i, t := range sourceTypes { + if len(targetTypes) < len(sourceTypes)-i { + return false + } + k, ok := slices.BinarySearchFunc(targetTypes, t, CompareTypes) + if !ok { + return false + } + targetTypes = targetTypes[k+1:] + } + return true + } + for _, t := range sourceTypes { + if !containsType(targetTypes, t) { return false } } @@ -3121,7 +3764,7 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError return TernaryFalse } // If source and target are already being compared, consider them related with assumptions - if r.maybeKeysSet.Has(id) { + if r.hasMaybeKey(id) { return TernaryMaybe } // A constrained key indicates that we have type references that reference constrained @@ -3129,7 +3772,7 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError // were unconstrained. if constrained { broadestEquivalentId, _ := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) - if r.maybeKeysSet.Has(broadestEquivalentId) { + if r.hasMaybeKey(broadestEquivalentId) { return TernaryMaybe } } @@ -3140,18 +3783,17 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError return TernaryMaybe } maybeStart := len(r.maybeKeys) - r.maybeKeys = append(r.maybeKeys, id) - r.maybeKeysSet.Add(id) + r.pushMaybeKey(id) saveExpandingFlags := r.expandingFlags if recursionFlags&RecursionFlagsSource != 0 { - r.sourceStack = append(r.sourceStack, source) - if r.expandingFlags&ExpandingFlagsSource == 0 && r.c.isDeeplyNestedType(source, r.sourceStack, 3) { + r.sourceStack = append(r.sourceStack, relaterStackEntry{t: source}) + if r.expandingFlags&ExpandingFlagsSource == 0 && r.c.isDeeplyNestedOnStack(source, r.sourceStack, 3) { r.expandingFlags |= ExpandingFlagsSource } } if recursionFlags&RecursionFlagsTarget != 0 { - r.targetStack = append(r.targetStack, target) - if r.expandingFlags&ExpandingFlagsTarget == 0 && r.c.isDeeplyNestedType(target, r.targetStack, 3) { + r.targetStack = append(r.targetStack, relaterStackEntry{t: target}) + if r.expandingFlags&ExpandingFlagsTarget == 0 && r.c.isDeeplyNestedOnStack(target, r.targetStack, 3) { r.expandingFlags |= ExpandingFlagsTarget } } @@ -3202,9 +3844,17 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError } func (r *Relater) resetMaybeStack(maybeStart int, propagatingVarianceFlags RelationComparisonResult, markAllAsSucceeded bool) { - for i := maybeStart; i < len(r.maybeKeys); i++ { - r.maybeKeysSet.Delete(r.maybeKeys[i]) - if markAllAsSucceeded { + if len(r.maybeKeys) > maybeKeysSetThreshold { + if maybeStart <= maybeKeysSetThreshold { + r.maybeKeysSet.Clear() + } else { + for i := maybeStart; i < len(r.maybeKeys); i++ { + r.maybeKeysSet.Delete(r.maybeKeys[i]) + } + } + } + if markAllAsSucceeded { + for i := maybeStart; i < len(r.maybeKeys); i++ { r.relation.set(r.maybeKeys[i], RelationComparisonResultSucceeded|propagatingVarianceFlags) r.relationCount-- } @@ -3249,7 +3899,7 @@ func (r *Relater) structuredTypeRelatedTo(source *Type, target *Type, reportErro sourceTypes = []*Type{source} } constraint := r.c.getEffectiveConstraintOfIntersection(sourceTypes, target.flags&TypeFlagsUnion != 0) - if constraint != nil && everyType(constraint, func(c *Type) bool { return c != source }) { + if constraint != nil && everyTypeNotEqual(constraint, source) { // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this result = r.isRelatedToEx(constraint, target, RecursionFlagsSource, false /*reportErrors*/, nil /*headMessage*/, intersectionState) } @@ -3286,60 +3936,64 @@ func (r *Relater) structuredTypeRelatedTo(source *Type, target *Type, reportErro return result } +func everyTypeNotEqual(t *Type, other *Type) bool { + if t.flags&TypeFlagsUnion != 0 { + return !slices.Contains(t.Types(), other) + } + return t != other +} + func (r *Relater) isSourceIntersectionNeedingExtraCheck(source *Type, target *Type) bool { - return source.flags&TypeFlagsIntersection != 0 && r.c.getApparentType(source).flags&TypeFlagsStructuredType != 0 && - !core.Some(source.Types(), func(t *Type) bool { - return t == target || t.objectFlags&ObjectFlagsNonInferrableType != 0 - }) + if source.flags&TypeFlagsIntersection == 0 || r.c.getApparentType(source).flags&TypeFlagsStructuredType == 0 { + return false + } + for _, t := range source.Types() { + if t == target || t.objectFlags&ObjectFlagsNonInferrableType != 0 { + return false + } + } + return true } -func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - var result Ternary - var varianceCheckFailed bool - var originalErrorChain *ErrorChain - saveErrorState := r.getErrorState() - relateVariances := func(sourceTypeArguments []*Type, targetTypeArguments []*Type, variances []VarianceFlags, intersectionState IntersectionState) (Ternary, bool) { - if result = r.typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, intersectionState); result != TernaryFalse { - return result, true - } - if core.Some(variances, func(v VarianceFlags) bool { return v&VarianceFlagsAllowsStructuralFallback != 0 }) { - // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we - // have to allow a structural fallback check - // We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially - // be assuming identity of the type parameter. - originalErrorChain = nil +func (r *Relater) relateVariances(sourceTypeArguments []*Type, targetTypeArguments []*Type, variances []VarianceFlags, reportErrors bool, intersectionState IntersectionState, saveErrorState errorState, varianceCheckFailed *bool, originalErrorChain **ErrorChain) (Ternary, bool) { + if result := r.typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, intersectionState); result != TernaryFalse { + return result, true + } + for _, v := range variances { + if v&VarianceFlagsAllowsStructuralFallback != 0 { + *originalErrorChain = nil r.restoreErrorState(saveErrorState) return TernaryFalse, false } - allowStructuralFallback := r.c.hasCovariantVoidArgument(targetTypeArguments, variances) - varianceCheckFailed = !allowStructuralFallback - // The type arguments did not relate appropriately, but it may be because we have no variance - // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type - // arguments). It might also be the case that the target type has a 'void' type argument for - // a covariant type parameter that is only used in return positions within the generic type - // (in which case any type argument is permitted on the source side). In those cases we proceed - // with a structural comparison. Otherwise, we know for certain the instantiations aren't - // related and we can return here. - if len(variances) != 0 && !allowStructuralFallback { - // In some cases generic types that are covariant in regular type checking mode become - // invariant in --strictFunctionTypes mode because one or more type parameters are used in - // both co- and contravariant positions. In order to make it easier to diagnose *why* such - // types are invariant, if any of the type parameters are invariant we reset the reported - // errors and instead force a structural comparison (which will include elaborations that - // reveal the reason). - // We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`, - // we can return `False` early here to skip calculating the structural error message we don't need. - if varianceCheckFailed && !(reportErrors && core.Some(variances, func(v VarianceFlags) bool { return (v & VarianceFlagsVarianceMask) == VarianceFlagsInvariant })) { + } + allowStructuralFallback := r.c.hasCovariantVoidArgument(targetTypeArguments, variances) + *varianceCheckFailed = !allowStructuralFallback + if len(variances) != 0 && !allowStructuralFallback { + if *varianceCheckFailed { + hasInvariant := false + if reportErrors { + for _, v := range variances { + if (v & VarianceFlagsVarianceMask) == VarianceFlagsInvariant { + hasInvariant = true + break + } + } + } + if !hasInvariant { return TernaryFalse, true } - // We remember the original error information so we can restore it in case the structural - // comparison unexpectedly succeeds. This can happen when the structural comparison result - // is a Ternary.Maybe for example caused by the recursion depth limiter. - originalErrorChain = r.errorChain - r.restoreErrorState(saveErrorState) } - return TernaryFalse, false + *originalErrorChain = r.errorChain + r.restoreErrorState(saveErrorState) } + return TernaryFalse, false +} + +func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { + var result Ternary + var varianceCheckFailed bool + var originalErrorChain *ErrorChain + saveErrorState := r.getErrorState() switch { case r.relation == r.c.identityRelation: // We've already checked that source.flags and target.flags are identical @@ -3435,7 +4089,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo nodeIsInJsFile := ast.IsInJSFile(source.alias.symbol.ValueDeclaration()) sourceTypes := r.c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams, nodeIsInJsFile) targetTypes := r.c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams, nodeIsInJsFile) - varianceResult, ok := relateVariances(sourceTypes, targetTypes, variances, intersectionState) + varianceResult, ok := r.relateVariances(sourceTypes, targetTypes, variances, reportErrors, intersectionState, saveErrorState, &varianceCheckFailed, &originalErrorChain) if ok { return varianceResult } @@ -3523,6 +4177,17 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo } case target.flags&TypeFlagsIndex != 0: targetType := target.AsIndexType().target + if r.c.isBinaryKeyedTypeLiteral(targetType) { + if source == target { + return TernaryTrue + } + if source.flags&TypeFlagsStringLiteral != 0 { + if targetType.symbol.LookupLazyMember(source.AsLiteralType().value.(string)) != nil { + return TernaryTrue + } + } + return TernaryFalse + } // A keyof S is related to a keyof T if T is related to S. if source.flags&TypeFlagsIndex != 0 { result = r.isRelatedTo(targetType, source.AsIndexType().target, RecursionFlagsBoth, false /*reportErrors*/) @@ -3575,7 +4240,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo case target.flags&TypeFlagsConditional != 0: // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive // conditional type and bail out with a Ternary.Maybe result. - if r.c.isDeeplyNestedType(target, r.targetStack, 10) { + if r.c.isDeeplyNestedOnStack(target, r.targetStack, 10) { return TernaryMaybe } c := target.AsConditionalType() @@ -3756,7 +4421,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo case source.flags&TypeFlagsConditional != 0: // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive // conditional type and bail out with a Ternary.Maybe result. - if r.c.isDeeplyNestedType(source, r.sourceStack, 10) { + if r.c.isDeeplyNestedOnStack(source, r.sourceStack, 10) { return TernaryMaybe } if target.flags&TypeFlagsConditional != 0 { @@ -3766,9 +4431,10 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo sourceParams := source.AsConditionalType().root.inferTypeParameters sourceExtends := source.AsConditionalType().extendsType var mapper *TypeMapper + var ctx *InferenceContext if len(sourceParams) != 0 { // If the source has infer type parameters, we instantiate them in the context of the target - ctx := r.c.newInferenceContext(sourceParams, nil /*signature*/, InferenceFlagsNone, r.isRelatedToWorker) + ctx = r.c.newInferenceContext(sourceParams, nil /*signature*/, InferenceFlagsNone, r.isRelatedToWorker) r.c.inferTypes(ctx.inferences, target.AsConditionalType().extendsType, sourceExtends, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false) sourceExtends = r.c.instantiateType(sourceExtends, ctx.mapper) mapper = ctx.mapper @@ -3779,9 +4445,15 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo result &= r.isRelatedTo(r.c.getFalseTypeFromConditionalType(source), r.c.getFalseTypeFromConditionalType(target), RecursionFlagsBoth, reportErrors) } if result != TernaryFalse { + if ctx != nil && !ctx.escaped { + r.c.releaseConditionalInferenceContext(ctx, nil, nil) + } return result } } + if ctx != nil && !ctx.escaped { + r.c.releaseConditionalInferenceContext(ctx, nil, nil) + } } // conditionals can be related to one another via normal constraint, as, eg, `A extends B ? O : never` should be assignable to `O` // when `O` is a conditional (`never` is trivially assignable to `O`, as is `O`!). @@ -3869,7 +4541,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo if len(variances) == 0 { return TernaryUnknown } - varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, intersectionState) + varianceResult, ok := r.relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, reportErrors, intersectionState, saveErrorState, &varianceCheckFailed, &originalErrorChain) if ok { return varianceResult } @@ -4284,27 +4956,45 @@ func (r *Relater) propertiesRelatedTo(source *Type, target *Type, reportErrors b } // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ // from the target union, across all members - properties := r.c.getPropertiesOfType(target) + properties, lazyTarget := r.c.getPropertiesOfTypeLazily(target) numericNamesOnly := isTupleType(source) && isTupleType(target) + resolvedSource := r.c.isResolvedPlainObjectType(source) for _, targetProp := range excludeProperties(properties, excludedProperties) { name := targetProp.Name() if targetProp.Flags()&ast.SymbolFlagsPrototype == 0 && (!numericNamesOnly || isNumericLiteralName(name) || name == "length") && (!optionalsOnly || targetProp.Flags()&ast.SymbolFlagsOptional != 0) { - sourceProp := r.c.getPropertyOfType(source, name) - if sourceProp != nil && sourceProp != targetProp { - related := r.propertyRelatedTo(source, target, sourceProp, targetProp, r.c.getNonMissingTypeOfSymbol, reportErrors, intersectionState, r.relation == r.c.comparableRelation) - if related == TernaryFalse { - return TernaryFalse + var sourceProp *ast.Symbol + if resolvedSource { + sourceProp = r.c.getResolvedPropertyOfPlainObjectType(source, name) + } else { + sourceProp = r.c.getPropertyOfType(source, name) + } + if sourceProp != nil { + if lazyTarget != nil { + targetProp = r.c.getPropertyOfType(lazyTarget, name) + } + if sourceProp != targetProp { + related := r.propertyRelatedTo(source, target, sourceProp, targetProp, nil, reportErrors, intersectionState, r.relation == r.c.comparableRelation) + if related == TernaryFalse { + return TernaryFalse + } + result &= related } - result &= related } } } return result } +func getPropertyAccessibilityFlags(s *ast.Symbol) ast.ModifierFlags { + if s.CheckFlags()&ast.CheckFlagsSynthetic == 0 && (s.Parent() == nil || s.Parent().Flags()&ast.SymbolFlagsClass == 0) { + return ast.ModifierFlagsNone + } + return getDeclarationModifierFlagsFromSymbol(s) +} + func (r *Relater) propertyRelatedTo(source *Type, target *Type, sourceProp *ast.Symbol, targetProp *ast.Symbol, getTypeOfSourceProperty func(sym *ast.Symbol) *Type, reportErrors bool, intersectionState IntersectionState, skipOptional bool) Ternary { - sourcePropFlags := getDeclarationModifierFlagsFromSymbol(sourceProp) - targetPropFlags := getDeclarationModifierFlagsFromSymbol(targetProp) + sourcePropFlags := getPropertyAccessibilityFlags(sourceProp) + targetPropFlags := getPropertyAccessibilityFlags(targetProp) switch { case sourcePropFlags&ast.ModifierFlagsPrivate != 0 || targetPropFlags&ast.ModifierFlagsPrivate != 0: if sourceProp.ValueDeclaration() != targetProp.ValueDeclaration() { @@ -4373,7 +5063,12 @@ func (r *Relater) isPropertySymbolTypeRelated(sourceProp *ast.Symbol, targetProp if effectiveTarget.flags&core.IfElse(r.relation == r.c.strictSubtypeRelation, TypeFlagsAny, TypeFlagsAnyOrUnknown) != 0 { return TernaryTrue } - effectiveSource := getTypeOfSourceProperty(sourceProp) + var effectiveSource *Type + if getTypeOfSourceProperty != nil { + effectiveSource = getTypeOfSourceProperty(sourceProp) + } else { + effectiveSource = r.c.getNonMissingTypeOfSymbol(sourceProp) + } return r.isRelatedToEx(effectiveSource, effectiveTarget, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) } @@ -4587,10 +5282,7 @@ func (r *Relater) signatureRelatedTo(source *Signature, target *Signature, erase source = r.c.getErasedSignature(source) target = r.c.getErasedSignature(target) } - isRelatedToWorker := func(source *Type, target *Type, reportErrors bool) Ternary { - return r.isRelatedToEx(source, target, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } - return r.c.compareSignaturesRelated(source, target, checkMode, reportErrors, r.reportError, isRelatedToWorker, r.c.reportUnreliableMapper) + return r.c.compareSignaturesRelated(source, target, checkMode, reportErrors, r, intersectionState) } func (r *Relater) signaturesIdenticalTo(source *Type, target *Type, kind SignatureKind) Ternary { diff --git a/tsc/internal/checker/services.go b/tsc/internal/checker/services.go index b20d1798e89be..7fdad6561d727 100644 --- a/tsc/internal/checker/services.go +++ b/tsc/internal/checker/services.go @@ -1,7 +1,6 @@ package checker import ( - "maps" "slices" "strings" @@ -23,7 +22,7 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) return nil } - symbols := make(ast.SymbolTable) + symbols := ast.NewSymbolTable() isStaticSymbol := false // Copy the given symbol into symbol tables if the symbol has the given meaning @@ -34,15 +33,15 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array - if _, ok := symbols[id]; !ok { - symbols[id] = symbol + if _, ok := symbols.Lookup(id); !ok { + symbols.Set(id, symbol) } } } copySymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { if meaning != 0 { - for _, symbol := range source { + for _, symbol := range source.All() { copySymbol(symbol, meaning) } } @@ -50,7 +49,7 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) copyLocallyVisibleExportSymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { if meaning != 0 { - for _, symbol := range source { + for _, symbol := range source.All() { // Similar condition as in `resolveNameHelper` if ast.GetDeclarationOfKind(symbol, ast.KindExportSpecifier) == nil && ast.GetDeclarationOfKind(symbol, ast.KindNamespaceExport) == nil && @@ -124,7 +123,7 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) populateSymbols() - delete(symbols, ast.InternalSymbolNameThis) // Not a symbol, a keyword + symbols.Delete(ast.InternalSymbolNameThis) // Not a symbol, a keyword return symbolsToArray(symbols) } @@ -133,7 +132,7 @@ func (c *Checker) GetExportsOfModule(symbol *ast.Symbol) []*ast.Symbol { } func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb func(*ast.Symbol, string)) { - for key, exportedSymbol := range c.getExportsOfModule(moduleSymbol) { + for key, exportedSymbol := range c.getExportsOfModule(moduleSymbol).All() { if !isReservedMemberName(key) { cb(exportedSymbol, key) } @@ -154,10 +153,17 @@ func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb if reducedType.flags&TypeFlagsStructuredType == 0 { return } - for name, symbol := range c.resolveStructuredTypeMembers(reducedType).getMembers() { - if c.isNamedMember(symbol, name) { - cb(symbol, name) + resolved := c.resolveStructuredTypeMembers(reducedType) + if m := resolved.getMembersIndex(); m != nil && m.fallback != nil { + for name, symbol := range m.fallback.All() { + if c.isNamedMember(symbol, name) { + cb(symbol, name) + } } + return + } + for _, symbol := range resolved.Properties() { + cb(symbol, symbol.Name()) } } @@ -212,20 +218,20 @@ func (c *Checker) GetAllPossiblePropertiesOfTypes(types []*Type) []*ast.Symbol { return c.getAugmentedPropertiesOfType(unionType) } - props := make(ast.SymbolTable) + props := ast.NewSymbolTable() for _, memberType := range types { augmentedProps := c.getAugmentedPropertiesOfType(memberType) for _, p := range augmentedProps { - if _, ok := props[p.Name()]; !ok { + if _, ok := props.Lookup(p.Name()); !ok { prop := c.createUnionOrIntersectionProperty(unionType, p.Name(), false /*skipObjectFunctionPropertyAugment*/) // May be undefined if the property is private if prop != nil { - props[p.Name()] = prop + props.Set(p.Name(), prop) } } } } - return slices.Collect(maps.Values(props)) + return slices.Collect(props.Values()) } func (c *Checker) IsUnknownSymbol(symbol *ast.Symbol) bool { @@ -258,11 +264,11 @@ func (c *Checker) GetElementTypeOfArrayType(t *Type) *Type { } func (c *Checker) GetCallSignatures(t *Type) []*Signature { - return c.getSignaturesOfType(t, SignatureKindCall) + return c.resolveExportedSignatures(c.getSignaturesOfType(t, SignatureKindCall)) } func (c *Checker) GetConstructSignatures(t *Type) []*Signature { - return c.getSignaturesOfType(t, SignatureKindConstruct) + return c.resolveExportedSignatures(c.getSignaturesOfType(t, SignatureKindConstruct)) } func (c *Checker) GetApparentProperties(t *Type) []*ast.Symbol { @@ -280,12 +286,12 @@ func (c *Checker) getAugmentedPropertiesOfType(t *Type) []*ast.Symbol { } if propsByName == nil { - propsByName = make(ast.SymbolTable) + propsByName = ast.NewSymbolTable() } if functionType != nil { for _, p := range c.getPropertiesOfType(functionType) { - if _, ok := propsByName[p.Name()]; !ok { - propsByName[p.Name()] = p + if _, ok := propsByName.Lookup(p.Name()); !ok { + propsByName.Set(p.Name(), p) } } } @@ -312,7 +318,7 @@ func (c *Checker) TryGetMemberInModuleExportsAndProperties(memberName string, mo func (c *Checker) TryGetMemberInModuleExports(memberName string, moduleSymbol *ast.Symbol) *ast.Symbol { symbolTable := c.getExportsOfModule(moduleSymbol) - return symbolTable[memberName] + return symbolTable.Get(memberName) } func (c *Checker) shouldTreatPropertiesOfExternalModuleAsExports(resolvedExternalModuleType *Type) bool { @@ -435,10 +441,9 @@ func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { return []*ast.Symbol{leftSpread, rightSpread} } } - if c.mappedSymbolLinks.Has(symbol) { - syntheticOrigin := c.mappedSymbolLinks.Get(symbol).syntheticOrigin - if syntheticOrigin != nil { - return []*ast.Symbol{syntheticOrigin} + if symbol.CheckFlags()&ast.CheckFlagsInstantiated == 0 { + if links := c.valueSymbolLinks.TryGet(symbol); links != nil && links.target != nil { + return []*ast.Symbol{links.target} } } target := c.tryGetTarget(symbol) @@ -903,7 +908,7 @@ func (c *Checker) getResolvedSignatureWorker(node *ast.Node, checkMode CheckMode res = c.getResolvedSignature(node, candidatesOutArray, checkMode) } c.apparentArgumentCount = nil - return res, *candidatesOutArray + return c.resolveExportedSignature(res), c.resolveExportedSignatures(*candidatesOutArray) } func (c *Checker) GetCandidateSignaturesForStringLiteralCompletions(call *ast.CallLikeExpression, editingArgument *ast.Node) []*Signature { diff --git a/tsc/internal/checker/symbolaccessibility.go b/tsc/internal/checker/symbolaccessibility.go index 389980a60ff64..204062db49de0 100644 --- a/tsc/internal/checker/symbolaccessibility.go +++ b/tsc/internal/checker/symbolaccessibility.go @@ -144,7 +144,7 @@ func (c *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *as c.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 { c.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ symbolTableID, _ bool, _ bool, _ *ast.Node) bool { found := false - for _, s := range t { + for _, s := range t.All() { if s.Flags()&leftMeaning != 0 && c.getTypeOfSymbol(s) == c.getDeclaredTypeOfSymbol(container) { variableMatches = append(variableMatches, s) found = true @@ -262,10 +262,10 @@ func (c *Checker) buildExternalModuleContainerIndex() { } container := c.getSymbolOfDeclaration(file.AsNode()) index.moduleOrder[container] = len(index.moduleOrder) - for _, exported := range c.getExportsOfSymbol(container) { + for _, exported := range c.getExportsOfSymbol(container).All() { index.add(c.getResolvedTarget(exported), container) } - if exportEquals := container.Exports()[ast.InternalSymbolNameExportEquals]; exportEquals != nil { + if exportEquals := container.Exports().Get(ast.InternalSymbolNameExportEquals); exportEquals != nil { index.add(c.getResolvedTarget(exportEquals), container) } } @@ -325,7 +325,7 @@ func (c *Checker) getFileSymbolIfFileSymbolExportEqualsContainer(d *ast.Node, co if fileSymbol == nil || fileSymbol.Exports() == nil { return nil } - exported, ok := fileSymbol.Exports()[ast.InternalSymbolNameExportEquals] + exported, ok := fileSymbol.Exports().Lookup(ast.InternalSymbolNameExportEquals) if !ok || exported == nil { return nil } @@ -409,18 +409,18 @@ func (c *Checker) getAliasForSymbolInContainer(container *ast.Symbol, symbol *as // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return // the container itself as the alias for the symbol if container.Exports() != nil { - exportEquals, ok := container.Exports()[ast.InternalSymbolNameExportEquals] + exportEquals, ok := container.Exports().Lookup(ast.InternalSymbolNameExportEquals) if ok && exportEquals != nil && c.getSymbolIfSameReference(exportEquals, symbol) != nil { return container } } exports := c.getExportsOfSymbol(container) - quick, ok := exports[symbol.Name()] + quick, ok := exports.Lookup(symbol.Name()) if ok && quick != nil && c.getSymbolIfSameReference(quick, symbol) != nil { return quick } var candidates []*ast.Symbol - for _, exported := range exports { + for _, exported := range exports.All() { if c.getSymbolIfSameReference(exported, symbol) != nil { candidates = append(candidates, exported) } @@ -580,7 +580,7 @@ func (c *Checker) getSymbolTableAliases(symbols ast.SymbolTable, tableId symbolT } } var aliases []*ast.Symbol - for _, sym := range symbols { + for _, sym := range symbols.All() { if sym.Flags()&ast.SymbolFlagsAlias != 0 { aliases = append(aliases, sym) } @@ -603,7 +603,7 @@ func (c *Checker) trySymbolTable( ) []*ast.Symbol { isGlobals := tableId == stKindGlobals // If symbol is directly available by its name in the symbol table - res, ok := symbols[ctx.symbol.Name()] + res, ok := symbols.Lookup(ctx.symbol.Name()) if ok && res != nil && c.isAccessible(ctx, res /*resolvedAliasSymbol*/, nil, ignoreQualification) { return []*ast.Symbol{ctx.symbol} } @@ -762,7 +762,7 @@ func (c *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration *a qualify := false c.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ symbolTableID, _ bool, _ bool, _ *ast.Node) bool { // If symbol of this name is not available in the symbol table we are ok - res, ok := symbolTable[symbol.Name()] + res, ok := symbolTable.Lookup(symbol.Name()) if !ok || res == nil { return false } @@ -847,12 +847,12 @@ func (c *Checker) someSymbolTableInScope( var table ast.SymbolTable sym := c.getSymbolOfDeclaration(location) // TODO: Should this filtered table be cached in some way? - for key, memberSymbol := range sym.Members() { + for key, memberSymbol := range sym.Members().All() { if memberSymbol.Flags()&(ast.SymbolFlagsType & ^ast.SymbolFlagsAssignment) != 0 { if table == nil { - table = make(ast.SymbolTable) + table = ast.NewSymbolTable() } - table[key] = memberSymbol + table.Set(key, memberSymbol) } } if table != nil && callback(table, symbolTableIDFromMembers(sym), false, false, location) { @@ -892,7 +892,7 @@ func (c *Checker) getClassExpressionNameTable(location *ast.Node) ast.SymbolTabl if len(nameText) == 0 || classSymbol == nil { return nil } - table := ast.SymbolTable{nameText: classSymbol} + table := ast.SymbolTableOf(nameText, classSymbol) if c.classExpressionNameTables == nil { c.classExpressionNameTables = make(map[ast.NodeId]ast.SymbolTable) } diff --git a/tsc/internal/checker/tracer.go b/tsc/internal/checker/tracer.go index 0fea57f8d4d70..bb9a138784fb5 100644 --- a/tsc/internal/checker/tracer.go +++ b/tsc/internal/checker/tracer.go @@ -191,7 +191,10 @@ func (a *tracedTypeAdapter) ConditionalTrueType() tracing.TracedType { if a.t.flags&TypeFlagsConditional == 0 { return nil } - t := a.t.AsConditionalType().resolvedTrueType + var t *Type + if ex := a.t.AsConditionalType().extra; ex != nil { + t = ex.resolvedTrueType + } if t == nil { return nil } @@ -202,7 +205,10 @@ func (a *tracedTypeAdapter) ConditionalFalseType() tracing.TracedType { if a.t.flags&TypeFlagsConditional == 0 { return nil } - t := a.t.AsConditionalType().resolvedFalseType + var t *Type + if ex := a.t.AsConditionalType().extra; ex != nil { + t = ex.resolvedFalseType + } if t == nil { return nil } @@ -323,7 +329,10 @@ func (a *tracedTypeAdapter) Pattern() *ast.Node { } func (a *tracedTypeAdapter) RecursionIdentity() any { - return getRecursionIdentity(a.t).value + if id := getRecursionIdentity(a.t); id != 0 { + return id + } + return nil } func (a *tracedTypeAdapter) Display() string { diff --git a/tsc/internal/checker/types.go b/tsc/internal/checker/types.go index f119288a21744..4eb9729b82685 100644 --- a/tsc/internal/checker/types.go +++ b/tsc/internal/checker/types.go @@ -194,21 +194,21 @@ type valueSymbolExtra struct { } func (links *ValueSymbolLinks) getWriteType() *Type { - if links.extra == nil { + if links == nil || links.extra == nil { return nil } return links.extra.writeType } func (links *ValueSymbolLinks) getNameType() *Type { - if links.extra == nil { + if links == nil || links.extra == nil { return nil } return links.extra.nameType } func (links *ValueSymbolLinks) getContainingType() *Type { - if links.extra == nil { + if links == nil || links.extra == nil { return nil } return links.extra.containingType @@ -244,11 +244,14 @@ func (links *ValueSymbolLinks) setContainingType(c *Checker, typ *Type) { links.extra.containingType = typ } -// Additional links for mapped symbols - -type MappedSymbolLinks struct { - keyType *Type // Key type for mapped type member - syntheticOrigin *ast.Symbol // For a property on a mapped or spread type, points back to the original property +func (links *ValueSymbolLinks) setMappedInfo(c *Checker, containingType *Type, nameType *Type, keyType *Type, syntheticOrigin *ast.Symbol) { + links.target = syntheticOrigin + if links.extra == nil { + links.extra = c.valueSymbolExtraArena.New() + } + links.extra.containingType = containingType + links.extra.nameType = nameType + links.extra.writeType = keyType } // Additional links for deferred type symbols @@ -299,8 +302,8 @@ type ExportTypeLinks struct { type TypeAliasLinks struct { declaredType *Type - typeParameters []*Type // Type parameters of type alias (undefined if non-generic) - instantiations map[CacheHashKey]*Type // Instantiations of generic type alias (undefined if non-generic) + typeParameters []*Type // Type parameters of type alias (undefined if non-generic) + instantiations typeCacheTable // Instantiations of generic type alias (undefined if non-generic) isConstructorDeclaredProperty bool } @@ -439,6 +442,7 @@ type SymbolNodeLinks struct { type TypeNodeLinks struct { resolvedType *Type // Resolved type associated with node + resolvedFlowType *Type // Result of applying getConditionalFlowTypeOfType to resolvedType outerTypeParameters *[]*Type // Outer type parameters of anonymous object type } @@ -496,9 +500,8 @@ type SourceFileLinks struct { // Signature specific links type SignatureLinks struct { - resolvedSignature *Signature // Cached signature of signature node or call expression - effectsSignature *Signature // Signature with possible control flow effects - decoratorSignature *Signature // Signature for decorator as if invoked by the runtime + resolvedSignature *Signature // Cached signature of signature node or call expression + effectsOrDecoratorSignature *Signature // Effects signature for call/binary expression or runtime signature for decorated declaration } type TypeFlags uint32 @@ -715,6 +718,8 @@ const ( ObjectFlagsIdenticalBaseTypeCalculated = 1 << 27 // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already ObjectFlagsIdenticalBaseTypeExists = 1 << 28 // has a defined cachedEquivalentBaseType member ObjectFlagsFromTypeNode = 1 << 29 // Originates in resolution of AST type node + ObjectFlagsHasGenericArgumentsComputed = 1 << 30 // HasGenericArguments flag has been computed + ObjectFlagsHasGenericArguments = 1 << 31 // Non-deferred type reference has generic type arguments // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution ObjectFlagsIsGenericTypeComputed = 1 << 22 // IsGenericObjectType flag has been computed ObjectFlagsIsGenericObjectType = 1 << 23 // Union or intersection contains generic object type @@ -783,11 +788,24 @@ const ( typeKindIntersection ) +type typeCacheFlags uint8 + +const ( + typeCacheIsWeakComputed typeCacheFlags = 1 << iota + typeCacheIsWeak + typeCacheShouldNormalizeComputed + typeCacheShouldNormalize + typeCacheIsReducibleIntersectionComputed + typeCacheIsReducibleIntersection + typeCacheNonNullableUnion +) + type Type struct { flags TypeFlags objectFlags ObjectFlags id TypeId kind typeKind + cacheFlags typeCacheFlags symbol *ast.Symbol alias *TypeAlias checker *Checker @@ -887,42 +905,42 @@ func (t *Type) AsConditionalType() *ConditionalType { // Casts for embedded struct types func (t *Type) AsConstrainedType() *ConstrainedType { - if t.kind >= typeKindTypeParameter && t.kind <= typeKindIntersection { + if t.kind >= typeKindTypeParameter { return (*ConstrainedType)(unsafe.Pointer(t)) } return nil } func (t *Type) AsStructuredType() *StructuredType { - if t.kind >= typeKindObject && t.kind <= typeKindIntersection { + if t.kind >= typeKindObject { return (*StructuredType)(unsafe.Pointer(t)) } return nil } func (t *Type) AsObjectType() *ObjectType { - if t.kind >= typeKindObject && t.kind <= typeKindEvolvingArray { + if t.kind-typeKindObject <= typeKindEvolvingArray-typeKindObject { return (*ObjectType)(unsafe.Pointer(t)) } return nil } func (t *Type) AsTypeReference() *TypeReference { - if t.kind >= typeKindTypeReference && t.kind <= typeKindTuple { + if t.kind-typeKindTypeReference <= typeKindTuple-typeKindTypeReference { return (*TypeReference)(unsafe.Pointer(t)) } return nil } func (t *Type) AsInterfaceType() *InterfaceType { - if t.kind >= typeKindInterface && t.kind <= typeKindTuple { + if t.kind-typeKindInterface <= typeKindTuple-typeKindInterface { return (*InterfaceType)(unsafe.Pointer(t)) } return nil } func (t *Type) AsUnionOrIntersectionType() *UnionOrIntersectionType { - if t.kind >= typeKindUnion && t.kind <= typeKindIntersection { + if t.kind >= typeKindUnion { return (*UnionOrIntersectionType)(unsafe.Pointer(t)) } return nil @@ -940,30 +958,43 @@ func (t *Type) Distributed() []*Type { // Common accessors +var typeTargetOffset = [typeKindIntersection + 1]uint8{ + typeKindObject: uint8(unsafe.Offsetof(ObjectType{}.target)), + typeKindTypeReference: uint8(unsafe.Offsetof(TypeReference{}.target)), + typeKindInterface: uint8(unsafe.Offsetof(InterfaceType{}.target)), + typeKindTuple: uint8(unsafe.Offsetof(TupleType{}.target)), + typeKindInstantiationExpression: uint8(unsafe.Offsetof(InstantiationExpressionType{}.target)), + typeKindMapped: uint8(unsafe.Offsetof(MappedType{}.target)), + typeKindReverseMapped: uint8(unsafe.Offsetof(ReverseMappedType{}.target)), + typeKindEvolvingArray: uint8(unsafe.Offsetof(EvolvingArrayType{}.target)), + typeKindTypeParameter: uint8(unsafe.Offsetof(TypeParameter{}.target)), + typeKindIndex: uint8(unsafe.Offsetof(IndexType{}.target)), + typeKindStringMapping: uint8(unsafe.Offsetof(StringMappingType{}.target)), +} + func (t *Type) Target() *Type { - switch { - case t.flags&TypeFlagsObject != 0: - return t.AsObjectType().target - case t.flags&TypeFlagsTypeParameter != 0: - return t.AsTypeParameter().target - case t.flags&TypeFlagsIndex != 0: - return t.AsIndexType().target - case t.flags&TypeFlagsStringMapping != 0: - return t.AsStringMappingType().target - case t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped != 0: - return t.AsMappedType().target + if off := typeTargetOffset[t.kind]; off != 0 { + return *(**Type)(unsafe.Add(unsafe.Pointer(&t.flags), off)) } panic("Unhandled case in Type.Target") } +var typeMapperOffset = [typeKindIntersection + 1]uint8{ + typeKindObject: uint8(unsafe.Offsetof(ObjectType{}.mapper)), + typeKindTypeReference: uint8(unsafe.Offsetof(TypeReference{}.mapper)), + typeKindInterface: uint8(unsafe.Offsetof(InterfaceType{}.mapper)), + typeKindTuple: uint8(unsafe.Offsetof(TupleType{}.mapper)), + typeKindInstantiationExpression: uint8(unsafe.Offsetof(InstantiationExpressionType{}.mapper)), + typeKindMapped: uint8(unsafe.Offsetof(MappedType{}.mapper)), + typeKindReverseMapped: uint8(unsafe.Offsetof(ReverseMappedType{}.mapper)), + typeKindEvolvingArray: uint8(unsafe.Offsetof(EvolvingArrayType{}.mapper)), + typeKindTypeParameter: uint8(unsafe.Offsetof(TypeParameter{}.mapper)), + typeKindConditional: uint8(unsafe.Offsetof(ConditionalType{}.mapper)), +} + func (t *Type) Mapper() *TypeMapper { - switch { - case t.flags&TypeFlagsObject != 0: - return t.AsObjectType().mapper - case t.flags&TypeFlagsTypeParameter != 0: - return t.AsTypeParameter().mapper - case t.flags&TypeFlagsConditional != 0: - return t.AsConditionalType().mapper + if off := typeMapperOffset[t.kind]; off != 0 { + return *(**TypeMapper)(unsafe.Add(unsafe.Pointer(&t.flags), off)) } panic("Unhandled case in Type.Mapper") } @@ -1114,71 +1145,81 @@ func (t *ConstrainedType) AsConstrainedType() *ConstrainedType { return t } type StructuredType struct { ConstrainedType - members ast.SymbolTable - properties []*ast.Symbol - extra *structuredTypeExtra + extra *structuredTypeExtra } type structuredTypeExtra struct { - signatures []*Signature // Signatures (call + construct) - callSignatureCount int // Count of call signatures - indexInfos []*IndexInfo - + members *memberIndex + properties []*ast.Symbol + signatures []*Signature // Signatures (call + construct) + callSignatureCount int // Count of call signatures + indexInfos []*IndexInfo objectTypeWithoutAbstractConstructSignatures *Type + regularType *Type } func (t *StructuredType) AsStructuredType() *StructuredType { return t } func (t *StructuredType) CallSignatures() []*Signature { - if t.extra == nil { + if t.extra == nil || t.extra.callSignatureCount == 0 { return nil } return slices.Clip(t.extra.signatures[:t.extra.callSignatureCount]) } func (t *StructuredType) ConstructSignatures() []*Signature { - if t.extra == nil { + if t.extra == nil || len(t.extra.signatures) == t.extra.callSignatureCount { return nil } return slices.Clip(t.extra.signatures[t.extra.callSignatureCount:]) } -func (t *StructuredType) getMember(name string) *ast.Symbol { - if t.members != nil { - return t.members[name] - } - lazy := t.getLazyState() - if lazy == nil { + +func (t *StructuredType) getSignatures() []*Signature { + if t.extra == nil { return nil } - memberInfo := lazy.layout.memberInfoMap[name] - if memberInfo == nil { + return t.extra.signatures +} + +func (t *StructuredType) getIndexInfos() []*IndexInfo { + if t.extra == nil { return nil } - return t.getPropertyAt(int(memberInfo.slotIndex)) + return t.extra.indexInfos } -func (t *StructuredType) getMembers() ast.SymbolTable { - if t.members == nil { - if properties := t.getProperties(); len(properties) != 0 { - t.members = make(ast.SymbolTable, len(properties)) - for _, member := range properties { - t.members[member.Name()] = member - } - } +func (t *StructuredType) ensureExtra() *structuredTypeExtra { + if t.extra == nil { + t.extra = t.checker.structuredTypeExtraArena.New() } - return t.members + return t.extra +} + +func (t *StructuredType) Properties() []*ast.Symbol { + return t.getProperties() +} + +func (t *StructuredType) getLazyState() *LazyState { + if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + return t.AsType().AsTypeReference().lazy + } + return nil } func (t *StructuredType) getPropertyAt(index int) *ast.Symbol { lazy := t.getLazyState() if lazy == nil { - return t.properties[index] + if t.extra == nil { + return nil + } + return t.extra.properties[index] } - if t.properties == nil { - t.properties = make([]*ast.Symbol, len(lazy.layout.memberInfos)) + ex := t.ensureExtra() + if ex.properties == nil { + ex.properties = t.checker.symbolListArena.NewSlice(len(lazy.layout.memberInfos)) } - member := t.properties[index] + member := ex.properties[index] if member == nil { memberInfo := lazy.layout.memberInfos[index] if memberInfo.baseIndex < 0 { @@ -1186,23 +1227,32 @@ func (t *StructuredType) getPropertyAt(index int) *ast.Symbol { } else { member = t.checker.getPropertyOfType(lazy.baseTypes[memberInfo.baseIndex], memberInfo.symbol.Name()) } - t.properties[index] = member + ex.properties[index] = member } return member } func (t *StructuredType) getProperties() []*ast.Symbol { if lazy := t.getLazyState(); lazy != nil && !lazy.propertiesResolved { - for i := range lazy.layout.memberInfos { - t.getPropertyAt(i) + if n := len(lazy.layout.memberInfos); n != 0 { + for i := range n { + t.getPropertyAt(i) + } } lazy.propertiesResolved = true } - return t.AsStructuredType().properties + if t.extra == nil { + return nil + } + return t.extra.properties +} + +func (t *StructuredType) getPropertiesSlice() []*ast.Symbol { + return t.getProperties() } func (t *StructuredType) hasProperties() bool { - if len(t.properties) != 0 { + if t.extra != nil && len(t.extra.properties) != 0 { return true } lazy := t.getLazyState() @@ -1212,36 +1262,228 @@ func (t *StructuredType) hasProperties() bool { return len(lazy.layout.memberInfos) != 0 } -func (t *StructuredType) getLazyState() *LazyState { - if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { - return t.AsType().AsTypeReference().lazy +func (t *StructuredType) getMembersIndex() *memberIndex { + if t.extra == nil { + return nil } - return nil + return t.extra.members } -func (t *StructuredType) getSignatures() []*Signature { - if t.extra == nil { +func (t *StructuredType) setMembersAndProperties(members *memberIndex, properties []*ast.Symbol) { + if members == nil && len(properties) == 0 && t.extra == nil { + return + } + ex := t.ensureExtra() + ex.members = members + ex.properties = properties +} + +type memberIndex struct { + slots []uint32 + fallback ast.SymbolTable +} + +// Property order is semantic. Keep it unchanged and index it with pointer-free +// slots instead of retaining a second map of names and symbol pointers. +func (c *Checker) newMemberIndex(members ast.SymbolTable, properties []*ast.Symbol, index *memberIndex) *memberIndex { + if members.Len() != len(properties) || !symbolTableKeysMatchNames(members) { + if index == nil { + index = c.memberIndexArena.New() + } + index.fallback = members + return index + } + if index != nil { + index.fallback = nil + } + return c.newPropertyIndex(properties, index) +} + +// Reports whether every symbol is stored under its own name. Checking the +// table's pairs, rather than looking each property up by name, also rejects a +// symbol aliased under a second key, which a name-keyed index would drop. +func symbolTableKeysMatchNames(members ast.SymbolTable) bool { + return !members.HasNameProxies() +} + +func hashMemberName(s string) uint64 { + n := len(s) + if n == 0 { + return 0 + } + p := unsafe.Pointer(unsafe.StringData(s)) + var h uint64 + if n >= 8 { + h = *(*uint64)(p) + for off := 8; off+8 < n; off += 8 { + h = (h ^ *(*uint64)(unsafe.Add(p, off))) * 0x9e3779b97f4a7c15 + } + h ^= *(*uint64)(unsafe.Add(p, n-8)) * 0xbf58476d1ce4e5b9 + } else if n >= 4 { + h = uint64(*(*uint32)(p)) | (uint64(*(*uint32)(unsafe.Add(p, n-4))) << 32) + } else { + h = uint64(*(*byte)(p)) | (uint64(*(*byte)(unsafe.Add(p, n>>1))) << 8) | (uint64(*(*byte)(unsafe.Add(p, n-1))) << 16) + } + h = (h ^ uint64(n)) * 0x94d049bb133111eb + return h ^ (h >> 32) +} + +func (c *Checker) newPropertyIndex(properties []*ast.Symbol, index *memberIndex) *memberIndex { + if len(properties) <= 3 { + if index != nil { + c.memberIndexArena.Pop(index) + } return nil } - return t.extra.signatures + if uint64(len(properties)) > uint64(^uint32(0)) { + panic("Member index exhausted") + } + if index == nil { + index = c.memberIndexArena.New() + } + size := 1 << bits.Len(uint(len(properties))*2-1) + index.slots = c.memberIndexSlotsArena.NewSlice(size) + mask := uint64(size - 1) + if len(properties) < 0xffff { + for i, symbol := range properties { + h := hashMemberName(symbol.Name()) + tag := uint32(h>>16) & 0xffff0000 + slot := h & mask + for index.slots[slot] != 0 { + slot = (slot + 1) & mask + } + index.slots[slot] = uint32(i+1) | tag + } + } else { + for i, symbol := range properties { + slot := hashMemberName(symbol.Name()) & mask + for index.slots[slot] != 0 { + slot = (slot + 1) & mask + } + index.slots[slot] = uint32(i + 1) + } + } + return index } -func (t *StructuredType) getIndexInfos() []*IndexInfo { +func (t *StructuredType) getMember(name string) *ast.Symbol { + if lazy := t.getLazyState(); lazy != nil { + if t.extra != nil && t.extra.members != nil && t.extra.members.fallback != nil { + return t.extra.members.fallback.Get(name) + } + slotIndex := lazy.layout.lookupMemberIndex(name) + if slotIndex < 0 { + return nil + } + return t.getPropertyAt(slotIndex) + } if t.extra == nil { return nil } - return t.extra.indexInfos + properties := t.extra.properties + members := t.extra.members + if members == nil { + for _, symbol := range properties { + if symbol.Name() == name { + return symbol + } + } + return nil + } + if members.fallback != nil { + return members.fallback.Get(name) + } + slots := members.slots + if len(slots) == 0 { + return nil + } + return t.getMemberFromSlots(name, hashMemberName(name), properties, slots) } -func (t *StructuredType) ensureExtra() *structuredTypeExtra { +func (t *StructuredType) getMemberWithHash(name string, h uint64) *ast.Symbol { + if lazy := t.getLazyState(); lazy != nil { + if t.extra != nil && t.extra.members != nil && t.extra.members.fallback != nil { + return t.extra.members.fallback.Get(name) + } + slotIndex := lazy.layout.lookupMemberIndex(name) + if slotIndex < 0 { + return nil + } + return t.getPropertyAt(slotIndex) + } if t.extra == nil { - t.extra = t.checker.structuredTypeExtraArena.New() + return nil + } + properties := t.extra.properties + members := t.extra.members + if members == nil { + for _, symbol := range properties { + if symbol.Name() == name { + return symbol + } + } + return nil + } + if members.fallback != nil { + return members.fallback.Get(name) + } + slots := members.slots + if len(slots) == 0 { + return nil + } + return t.getMemberFromSlots(name, h, properties, slots) +} + +func (t *StructuredType) getMemberFromSlots(name string, h uint64, properties []*ast.Symbol, slots []uint32) *ast.Symbol { + mask := uint64(len(slots) - 1) + slot := h & mask + if len(properties) < 0xffff { + tag := uint32(h>>16) & 0xffff0000 + for { + entry := slots[slot] + if entry == 0 { + return nil + } + if (entry & 0xffff0000) == tag { + symbol := properties[(entry&0xffff)-1] + if symbol.Name() == name { + return symbol + } + } + slot = (slot + 1) & mask + } + } + for { + index := slots[slot] + if index == 0 { + return nil + } + symbol := properties[index-1] + if symbol.Name() == name { + return symbol + } + slot = (slot + 1) & mask } - return t.extra } -func (t *StructuredType) Properties() []*ast.Symbol { - return t.getProperties() +func (t *StructuredType) getMembers() ast.SymbolTable { + if t.extra != nil && t.extra.members != nil && t.extra.members.fallback != nil { + return t.extra.members.fallback + } + properties := t.getProperties() + if len(properties) == 0 { + return nil + } + members := ast.NewSymbolTableWithCapacity(len(properties)) + for _, symbol := range properties { + members.Set(symbol.Name(), symbol) + } + ex := t.ensureExtra() + if ex.members == nil { + ex.members = t.checker.memberIndexArena.New() + } + ex.members.fallback = members + return members } // Except for tuple type references and reverse mapped types, all object types have an associated symbol. @@ -1279,22 +1521,15 @@ func (t *StructuredType) Properties() []*ast.Symbol { type ObjectType struct { StructuredType - target *Type // Target of instantiated type - mapper *TypeMapper // Type mapper for instantiated type - instantiations map[CacheHashKey]*Type // Map of type instantiations + target *Type // Target of instantiated type + mapper *TypeMapper // Type mapper for instantiated type + instantiations *typeCacheTable // Map of type instantiations } func (t *ObjectType) AsObjectType() *ObjectType { return t } // TypeReference (instantiation of an InterfaceType) -type TypeReference struct { - ObjectType - node *ast.Node // TypeReferenceNode | ArrayTypeNode | TupleTypeNode when deferred, else nil - resolvedTypeArguments []*Type - lazy *LazyState // Resolved member layout and instantiation state -} - type LazyState struct { layout *MemberLayout mapper *TypeMapper @@ -1303,16 +1538,45 @@ type LazyState struct { } type MemberLayout struct { - id uint32 // Unique identifier for the member layout - memberInfoMap map[string]*MemberInfo // MemberInfos keyed by property name - memberInfos []MemberInfo // MemberInfos in sorted order - layoutProperties []*ast.Symbol // Lazily created list of layout properties (symbols corresponding to memberInfos) + id uint32 // Unique identifier for the member layout + memberInfoMap map[string]int32 // 1-based slotIndex keyed by property name (nil when len(memberInfos) <= 3) + memberInfos []MemberInfo // MemberInfos in sorted order + layoutProperties []*ast.Symbol // Lazily created list of layout properties (symbols corresponding to memberInfos) +} + +func (l *MemberLayout) lookupMemberIndex(name string) int { + if l.memberInfoMap != nil { + return int(l.memberInfoMap[name]) - 1 + } + for i := range l.memberInfos { + if l.memberInfos[i].symbol.Name() == name { + return i + } + } + return -1 } type MemberInfo struct { symbol *ast.Symbol // Uninstantiated symbol baseIndex int32 // Index of base type with symbol, or -1 if locally declared - slotIndex int32 // Index of resolved symbol in members array +} + +type lazyMappedTable struct { + typeParameter *Type + modifiersType *Type + templateModifiers MappedTypeModifiers + shouldLinkPropDeclarations bool + members map[string]*ast.Symbol + indexInfos []*IndexInfo + indexInfosReady bool + resolving bool +} + +type TypeReference struct { + ObjectType + node *ast.Node // TypeReferenceNode | ArrayTypeNode | TupleTypeNode when deferred, else nil + resolvedTypeArguments []*Type + lazy *LazyState // Resolved member layout and instantiation state } func (t *TypeReference) AsTypeReference() *TypeReference { return t } @@ -1326,6 +1590,8 @@ type InterfaceType struct { thisType *Type // The "this" type (nil if none) baseTypesResolved bool declaredMembersResolved bool + declaredMemberLayoutCount int32 + declaredMemberLayout *MemberLayout resolvedBaseConstructorType *Type resolvedBaseTypes []*Type declaredMembers ast.SymbolTable // Declared members @@ -1423,6 +1689,7 @@ type MappedType struct { templateType *Type modifiersType *Type resolvedApparentType *Type + lazy *lazyMappedTable containsError bool } @@ -1459,9 +1726,8 @@ type EvolvingArrayType struct { type UnionOrIntersectionType struct { StructuredType types []*Type - propertyCache ast.SymbolTable + propertyCache ast.SymbolTable // Augmented entries not shared with the non-augmented cache. propertyCacheWithoutFunctionPropertyAugment ast.SymbolTable - resolvedProperties []*ast.Symbol } func (t *UnionOrIntersectionType) AsUnionOrIntersectionType() *UnionOrIntersectionType { return t } @@ -1475,18 +1741,14 @@ func (t *UnionOrIntersectionType) Types() []*Type { type UnionType struct { UnionOrIntersectionType resolvedReducedType *Type - regularType *Type - origin *Type // Denormalized union, intersection, or index type in which union originates - keyPropertyName string // Property with unique unit type that exists in every object/intersection in union type - constituentMap map[*Type]*Type // Constituents keyed by unit type discriminants + origin *Type // Denormalized union, intersection, or index type in which union originates } // IntersectionType type IntersectionType struct { UnionOrIntersectionType - resolvedApparentType *Type - uniqueLiteralFilledInstantiation *Type // Instantiation with type parameters mapped to never type + resolvedApparentType *Type } // TypeParameter @@ -1569,24 +1831,35 @@ type ConditionalRoot struct { isDistributive bool inferTypeParameters []*Type outerTypeParameters []*Type - instantiations map[CacheHashKey]*Type + instantiations typeCacheTable alias *TypeAlias } -type ConditionalType struct { - ConstrainedType - root *ConditionalRoot - checkType *Type - extendsType *Type +type conditionalTypeExtra struct { resolvedTrueType *Type resolvedFalseType *Type resolvedInferredTrueType *Type // The `trueType` instantiated with the `combinedMapper`, if present resolvedDefaultConstraint *Type resolvedConstraintOfDistributive *Type - mapper *TypeMapper combinedMapper *TypeMapper } +type ConditionalType struct { + ConstrainedType + root *ConditionalRoot + checkType *Type + extendsType *Type + mapper *TypeMapper + extra *conditionalTypeExtra +} + +func (t *ConditionalType) ensureExtra(c *Checker) *conditionalTypeExtra { + if t.extra == nil { + t.extra = c.conditionalTypeExtraArena.New() + } + return t.extra +} + func (t *ConditionalType) CheckType() *Type { return t.checkType } func (t *ConditionalType) ExtendsType() *Type { return t.extendsType } @@ -1608,6 +1881,9 @@ const ( SignatureFlagsIsNonInferrable SignatureFlags = 1 << 7 // Indicates signature comes from a non-inferrable type SignatureFlagsIsSignatureCandidateForOverloadFailure SignatureFlags = 1 << 8 signatureFlagsNoTypePredicate SignatureFlags = 1 << 9 + signatureFlagsUninstantiatedParameters SignatureFlags = 1 << 10 + signatureFlagsUninstantiatedThisParameter SignatureFlags = 1 << 11 + signatureFlagsInternal SignatureFlags = signatureFlagsNoTypePredicate | signatureFlagsUninstantiatedParameters | signatureFlagsUninstantiatedThisParameter // We do not propagate `IsInnerCallChain` or `IsOuterCallChain` to instantiated signatures, as that would result in us // attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when // instantiating the return type. @@ -1625,6 +1901,7 @@ type Signature struct { declaration *ast.Node parameters []*ast.Symbol resolvedReturnType *Type + firstParamType *Type target *Signature mapper *TypeMapper extra *signatureExtra @@ -1636,6 +1913,7 @@ type signatureExtra struct { resolvedTypePredicate *TypePredicate isolatedSignatureType *Type composite *CompositeSignature + paramTypes []*Type } func (s *Signature) ensureExtra(c *Checker) *signatureExtra { @@ -1652,6 +1930,7 @@ func (s *Signature) setTypeParameters(c *Checker, types []*Type) { } func (s *Signature) setThisParameter(c *Checker, parameter *ast.Symbol) { + s.flags &^= signatureFlagsUninstantiatedThisParameter if parameter != nil || s.extra != nil { s.ensureExtra(c).thisParameter = parameter } @@ -1703,7 +1982,7 @@ func (s *Signature) Id() SignatureId { } func (s *Signature) Flags() SignatureFlags { - return s.flags &^ signatureFlagsNoTypePredicate + return s.flags &^ signatureFlagsInternal } func (s *Signature) TypeParameters() []*Type { diff --git a/tsc/internal/checker/types_test.go b/tsc/internal/checker/types_test.go index 86de2b089e06e..4137791cf9783 100644 --- a/tsc/internal/checker/types_test.go +++ b/tsc/internal/checker/types_test.go @@ -98,7 +98,18 @@ func TestTypeCasts(t *testing.T) { typ.flags = TypeFlagsNever typ.objectFlags = ObjectFlagsReference assert.Equal(t, test.cast(typ), any(test.data)) - assert.Assert(t, didPanic(func() { typ.AsLiteralType() }) == (test.kind != typeKindLiteral)) + for _, target := range tests { + switch target.kind { + case typeKindObject: + assert.Equal(t, target.cast(typ), any(test.data.AsObjectType())) + case typeKindTypeReference: + assert.Equal(t, target.cast(typ), any(test.data.AsTypeReference())) + case typeKindInterface: + assert.Equal(t, target.cast(typ), any(test.data.AsInterfaceType())) + default: + assert.Assert(t, didPanic(func() { target.cast(typ) }) == (test.kind != target.kind)) + } + } } } @@ -142,6 +153,79 @@ func TestKeyBuilder(t *testing.T) { } } +func TestRelationKeys(t *testing.T) { + t.Parallel() + source := &Type{id: 1} + target := &Type{id: ^TypeId(0)} + var relation Relation + assert.Equal(t, relation.size(), 0) + for state := IntersectionStateNone; state <= IntersectionStateSource|IntersectionStateTarget; state++ { + key, constrained := getRelationKey(source, target, state, false, false) + assert.Assert(t, !constrained) + assert.Equal(t, relation.get(key), RelationComparisonResultNone) + relation.set(key, RelationComparisonResultSucceeded) + reverse, _ := getRelationKey(target, source, state, false, false) + assert.Assert(t, key != reverse) + assert.Equal(t, relation.get(reverse), RelationComparisonResultNone) + identity, _ := getRelationKey(target, source, state, true, false) + assert.Equal(t, key, identity) + relation.set(key, RelationComparisonResultFailed|RelationComparisonResultReportsUnreliable) + assert.Equal(t, relation.get(key), RelationComparisonResultFailed|RelationComparisonResultReportsUnreliable) + } + assert.Equal(t, relation.size(), 4) + for state := IntersectionStateNone; state <= IntersectionStateSource|IntersectionStateTarget; state++ { + key, _ := getRelationKey(source, target, state, false, false) + assert.Equal(t, relation.get(key), RelationComparisonResultFailed|RelationComparisonResultReportsUnreliable) + } +} + +func TestRelationKeyNamespaces(t *testing.T) { + t.Parallel() + simple, _ := getRelationKey(&Type{id: 1}, &Type{id: 2}, IntersectionStateNone, false, false) + generic := relationKey{value: simple.value, generic: true} + var relation Relation + relation.set(simple, RelationComparisonResultSucceeded) + assert.Equal(t, relation.get(generic), RelationComparisonResultNone) + relation.set(generic, RelationComparisonResultFailed) + assert.Equal(t, relation.get(simple), RelationComparisonResultSucceeded) + assert.Equal(t, relation.get(generic), RelationComparisonResultFailed) + assert.Equal(t, relation.size(), 2) +} + +func TestGenericRelationKeys(t *testing.T) { + t.Parallel() + c, types := newCompositeTestChecker() + c.noConstraintType = c.newIntrinsicType(TypeFlagsNever, "no constraint") + root := types[0] + newParameter := func(constraint *Type) *Type { + parameter := &TypeParameter{constraint: constraint} + return c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, parameter.AsType(), typeKindTypeParameter) + } + newReference := func(argument *Type) *Type { + reference := &TypeReference{resolvedTypeArguments: []*Type{argument}} + reference.target = root + return c.newType(TypeFlagsObject, ObjectFlagsReference, reference.AsType(), typeKindTypeReference) + } + left := newReference(newParameter(c.noConstraintType)) + right := newReference(newParameter(c.noConstraintType)) + equivalentLeft := newReference(newParameter(c.noConstraintType)) + equivalentRight := newReference(newParameter(c.noConstraintType)) + key, constrained := getRelationKey(left, right, IntersectionStateNone, false, false) + assert.Assert(t, !constrained) + equivalent, _ := getRelationKey(equivalentLeft, equivalentRight, IntersectionStateNone, false, false) + assert.Equal(t, key, equivalent) + var relation Relation + relation.set(key, RelationComparisonResultSucceeded) + assert.Equal(t, relation.get(equivalent), RelationComparisonResultSucceeded) + constrainedLeft := newReference(newParameter(types[1])) + constrainedKey, constrained := getRelationKey(constrainedLeft, right, IntersectionStateNone, false, false) + assert.Assert(t, constrained) + assert.Assert(t, constrainedKey != key) + broadest, constrained := getRelationKey(constrainedLeft, right, IntersectionStateNone, false, true) + assert.Assert(t, !constrained) + assert.Equal(t, broadest, key) +} + func TestTypeListKeyAllocations(t *testing.T) { //nolint:paralleltest if race.Enabled { t.Skip("race instrumentation changes hashing allocations") @@ -178,9 +262,7 @@ func BenchmarkTypeListKey(b *testing.B) { func newCompositeTestChecker() (*Checker, []*Type) { c := &Checker{ strictNullChecks: true, - unionTypes: make(map[CacheHashKey]*Type), unionOfUnionTypes: make(map[UnionOfUnionKey]*Type), - intersectionTypes: make(map[CacheHashKey]*Type), subtypeReductionCache: make(map[CacheHashKey][]*Type), } c.compareSymbols = c.compareSymbolsWorker @@ -291,10 +373,10 @@ func TestIntersectionReductionAllocations(t *testing.T) { //nolint:paralleltest t.Skip("race instrumentation changes hashing allocations") } c, _ := newCompositeTestChecker() - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() for i := range 128 { name := strconv.Itoa(i) - members[name] = c.newSymbol(ast.SymbolFlagsProperty, name) + members.Set(name, c.newSymbol(ast.SymbolFlagsProperty, name)) } object := c.newAnonymousType(nil, members, nil, nil, nil) intersection := c.newIntersectionType(ObjectFlagsNone, []*Type{object, object}) @@ -307,33 +389,40 @@ func TestIntersectionReductionAllocations(t *testing.T) { //nolint:paralleltest func TestTemporaryPropertyCounts(t *testing.T) { t.Parallel() c := &Checker{} + names := func(p *temporaryPropertyCounts) []string { + var result []string + for _, e := range p.entries { + result = append(result, e.name) + } + return result + } outer := c.getTemporaryPropertyCounts() - outer.Set("second", 1) - outer.Set("first", 1) - outer.Set("second", 2) - assert.Assert(t, slices.Equal(slices.Collect(outer.Keys()), []string{"second", "first"})) + outer.Add("second") + outer.Add("first") + outer.Add("second") + assert.Assert(t, slices.Equal(names(outer), []string{"second", "first"})) inner := c.getTemporaryPropertyCounts() assert.Assert(t, inner != outer) - inner.Set("first", 4) - assert.Equal(t, outer.GetOrZero("first"), 1) - assert.Equal(t, outer.GetOrZero("second"), 2) + inner.Add("first") + assert.Equal(t, outer.Count("first"), 1) + assert.Equal(t, outer.Count("second"), 2) c.releaseTemporaryPropertyCounts(inner) assert.Equal(t, inner.Size(), 0) - assert.Assert(t, !inner.Has("first")) + assert.Equal(t, inner.Count("first"), 0) reused := c.getTemporaryPropertyCounts() assert.Equal(t, reused, inner) - reused.Set("second", 3) - reused.Set("first", 5) - assert.Assert(t, slices.Equal(slices.Collect(reused.Keys()), []string{"second", "first"})) - assert.Equal(t, outer.GetOrZero("first"), 1) + reused.Add("second") + reused.Add("first") + assert.Assert(t, slices.Equal(names(reused), []string{"second", "first"})) + assert.Equal(t, outer.Count("first"), 1) c.releaseTemporaryPropertyCounts(reused) c.releaseTemporaryPropertyCounts(outer) runtime.GC() reused = c.getTemporaryPropertyCounts() assert.Equal(t, reused.Size(), 0) - assert.Assert(t, !reused.Has("second")) + assert.Equal(t, reused.Count("second"), 0) for i := range 4097 { - reused.Set(strconv.Itoa(i), i) + reused.Add(strconv.Itoa(i)) } c.releaseTemporaryPropertyCounts(reused) for counts := c.freePropertyCounts; counts != nil; counts = counts.next { @@ -349,7 +438,7 @@ func TestIntersectionReductionReentrancy(t *testing.T) { t.Parallel() c, _ := newCompositeTestChecker() property := c.newSymbol(ast.SymbolFlagsProperty, "value") - object := c.newAnonymousType(nil, ast.SymbolTable{"value": property}, nil, nil, nil) + object := c.newAnonymousType(nil, ast.SymbolTableOf("value", property), nil, nil, nil) inner := c.newIntersectionType(ObjectFlagsNone, []*Type{object, object}) outer := c.newIntersectionType(ObjectFlagsNone, []*Type{inner, object}) assert.Equal(t, c.getReducedType(outer), outer) @@ -359,12 +448,12 @@ func TestIntersectionReductionReentrancy(t *testing.T) { assert.Assert(t, c.freePropertyCounts.next != nil) workspace := c.freePropertyCounts conflicting := c.newSymbolEx(ast.SymbolFlagsProperty, "value", ast.CheckFlagsContainsPrivate) - outer.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment["value"] = conflicting + outer.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment.Set("value", conflicting) assert.Assert(t, c.somePropertyReducesToNever(outer)) assert.Equal(t, c.freePropertyCounts, workspace) for counts := c.freePropertyCounts; counts != nil; counts = counts.next { assert.Equal(t, counts.Size(), 0) - assert.Assert(t, !counts.Has("value")) + assert.Equal(t, counts.Count("value"), 0) } } @@ -441,10 +530,30 @@ func TestCompactTypeMapperLayout(t *testing.T) { c, types := newCompositeTestChecker() mapper := newSimpleTypeMapper(types[0], types[1]) assert.Assert(t, castTypeMapper[SimpleTypeMapper](mapper, TypeMapperKindSimple).source == types[0]) - func() { - defer func() { assert.Assert(t, recover() != nil) }() - castTypeMapper[ArrayTypeMapper](mapper, TypeMapperKindArray) - }() + context := c.newInferenceContextWorker(nil, nil, InferenceFlagsNone, nil) + tests := []struct { + kind TypeMapperKind + mapper *TypeMapper + cast func(*TypeMapper) any + }{ + {TypeMapperKindSimple, mapper, func(m *TypeMapper) any { return castTypeMapper[SimpleTypeMapper](m, TypeMapperKindSimple) }}, + {TypeMapperKindArray, newArrayTypeMapper(nil, nil), func(m *TypeMapper) any { return castTypeMapper[ArrayTypeMapper](m, TypeMapperKindArray) }}, + {TypeMapperKindArrayToSingle, newArrayToSingleTypeMapper(nil, nil), func(m *TypeMapper) any { + return castTypeMapper[ArrayToSingleTypeMapper](m, TypeMapperKindArrayToSingle) + }}, + {TypeMapperKindDeferred, newDeferredTypeMapper(nil, nil), func(m *TypeMapper) any { return castTypeMapper[DeferredTypeMapper](m, TypeMapperKindDeferred) }}, + {TypeMapperKindFunction, newFunctionTypeMapper(func(t *Type) *Type { return t }), func(m *TypeMapper) any { return castTypeMapper[FunctionTypeMapper](m, TypeMapperKindFunction) }}, + {TypeMapperKindMerged, newMergedTypeMapper(mapper, mapper), func(m *TypeMapper) any { return castTypeMapper[MergedTypeMapper](m, TypeMapperKindMerged) }}, + {TypeMapperKindComposite, newCompositeTypeMapper(c, mapper, mapper), func(m *TypeMapper) any { return castTypeMapper[CompositeTypeMapper](m, TypeMapperKindComposite) }}, + {TypeMapperKindInference, context.mapper, func(m *TypeMapper) any { return castTypeMapper[InferenceTypeMapper](m, TypeMapperKindInference) }}, + } + runtime.GC() + for _, test := range tests { + assert.Equal(t, test.mapper.kind, test.kind) + for _, target := range tests { + assert.Assert(t, didPanic(func() { target.cast(test.mapper) }) == (test.kind != target.kind)) + } + } runtime.GC() assert.Equal(t, mapper.Map(types[0]), types[1]) runtime.KeepAlive(c) @@ -507,9 +616,137 @@ func TestCompactValueSymbolLinksLayout(t *testing.T) { assert.Equal(t, links.getContainingType(), types[2]) } +func TestStructuredMembersLookup(t *testing.T) { + t.Parallel() + c, _ := newCompositeTestChecker() + for _, count := range []int{0, 1, 8, 20, 300} { + members := ast.NewSymbolTableWithCapacity(count) + for i := range count { + name := strconv.Itoa(i) + members.Set(name, c.newSymbol(ast.SymbolFlagsProperty, name)) + } + typ := c.newAnonymousType(nil, members, nil, nil, nil) + resolved := typ.AsStructuredType() + assert.Equal(t, len(resolved.Properties()), count) + for name, symbol := range members.All() { + assert.Equal(t, resolved.getMember(name), symbol) + } + assert.Assert(t, resolved.getMember("missing") == nil) + other := c.newAnonymousType(nil, resolved.getMembers(), nil, nil, nil) + for name, symbol := range members.All() { + assert.Equal(t, other.AsStructuredType().getMember(name), symbol) + } + members.Set("new", c.newSymbol(ast.SymbolFlagsProperty, "new")) + assert.Assert(t, resolved.getMember("new") == nil) + members.Set(ast.InternalSymbolNameCall, c.newSymbol(ast.SymbolFlagsSignature, ast.InternalSymbolNameCall)) + withReserved := c.newAnonymousType(nil, members, nil, nil, nil).AsStructuredType() + assert.Equal(t, withReserved.getMember(ast.InternalSymbolNameCall), members.Get(ast.InternalSymbolNameCall)) + for range 300 { + c.newMemberIndex(members, withReserved.Properties(), nil) + } + runtime.GC() + for name, symbol := range members.All() { + assert.Equal(t, withReserved.getMember(name), symbol) + } + } + renamed := c.newSymbol(ast.SymbolFlagsProperty, "original") + member := c.newAnonymousType(nil, ast.SymbolTableOf("different", renamed), nil, nil, nil).AsStructuredType() + assert.Equal(t, member.getMember("different"), renamed) + assert.Assert(t, member.getMember("original") == nil) + aliased := c.newSymbol(ast.SymbolFlagsProperty, "original") + aliasedMembers := ast.NewSymbolTable() + aliasedMembers.Set("original", aliased) + aliasedMembers.Set("different", aliased) + for i := range 10 { + name := "p" + strconv.Itoa(i) + aliasedMembers.Set(name, c.newSymbol(ast.SymbolFlagsProperty, name)) + } + aliasedType := c.newAnonymousType(nil, aliasedMembers, nil, nil, nil).AsStructuredType() + for name, symbol := range aliasedMembers.All() { + assert.Equal(t, aliasedType.getMember(name), symbol) + } +} + +func TestUnionIntersectionPropertyCacheSharing(t *testing.T) { + t.Parallel() + for _, intersection := range []bool{false, true} { + for _, augmentedFirst := range []bool{false, true} { + t.Run(strconv.FormatBool(intersection)+"/"+strconv.FormatBool(augmentedFirst), func(t *testing.T) { + t.Parallel() + c, _ := newCompositeTestChecker() + members := ast.NewSymbolTable() + for i := range 300 { + name := strconv.Itoa(i) + members.Set(name, c.newSymbol(ast.SymbolFlagsProperty, name)) + } + members.Set("", c.newSymbol(ast.SymbolFlagsProperty, "")) + object := c.newAnonymousType(nil, members, nil, nil, nil) + var typ *Type + if intersection { + typ = c.newIntersectionType(ObjectFlagsNone, []*Type{object, object}) + } else { + typ = c.newUnionType(ObjectFlagsNone, []*Type{object, object}) + } + data := typ.AsUnionOrIntersectionType() + for name, symbol := range members.All() { + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, !augmentedFirst), symbol) + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, augmentedFirst), symbol) + } + assert.Equal(t, data.propertyCacheWithoutFunctionPropertyAugment.Len(), members.Len()) + assert.Assert(t, data.propertyCache == nil) + runtime.GC() + for name, symbol := range members.All() { + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, true), symbol) + } + runtime.KeepAlive(c) + }) + } + } +} + +func TestUnionIntersectionPropertyCacheModes(t *testing.T) { + t.Parallel() + for _, intersection := range []bool{false, true} { + t.Run(strconv.FormatBool(intersection), func(t *testing.T) { + t.Parallel() + c, _ := newCompositeTestChecker() + var typ *Type + if intersection { + typ = c.newIntersectionType(ObjectFlagsNone, nil) + } else { + typ = c.newUnionType(ObjectFlagsNone, nil) + } + data := typ.AsUnionOrIntersectionType() + assert.Assert(t, c.getUnionOrIntersectionProperty(typ, "missing", true) == nil) + assert.Assert(t, c.getUnionOrIntersectionProperty(typ, "missing", false) == nil) + assert.Assert(t, data.propertyCache == nil) + assert.Assert(t, data.propertyCacheWithoutFunctionPropertyAugment == nil) + data.propertyCacheWithoutFunctionPropertyAugment = ast.NewSymbolTable() + data.propertyCache = ast.NewSymbolTable() + for _, flags := range []ast.CheckFlags{ast.CheckFlagsReadPartial, ast.CheckFlagsWritePartial} { + name := strconv.Itoa(int(flags)) + partial := c.newSymbolEx(ast.SymbolFlagsProperty, name, flags) + complete := c.newSymbol(ast.SymbolFlagsProperty, name) + data.propertyCacheWithoutFunctionPropertyAugment.Set(name, partial) + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, true), partial) + assert.Assert(t, c.getUnionOrIntersectionProperty(typ, name, false) == nil) + assert.Equal(t, c.getPropertyOfUnionOrIntersectionType(typ, name, true) == nil, flags&ast.CheckFlagsReadPartial != 0) + data.propertyCache.Set(name, complete) + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, false), complete) + } + nonAugmented := c.newSymbol(ast.SymbolFlagsProperty, "different") + augmented := c.newSymbol(ast.SymbolFlagsProperty, "different") + data.propertyCacheWithoutFunctionPropertyAugment.Set("different", nonAugmented) + data.propertyCache.Set("different", augmented) + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, "different", true), nonAugmented) + assert.Equal(t, c.getUnionOrIntersectionProperty(typ, "different", false), augmented) + }) + } +} + func TestCompactStructuredTypeLayout(t *testing.T) { t.Parallel() - assert.Equal(t, unsafe.Sizeof(StructuredType{}), unsafe.Sizeof(ConstrainedType{})+5*unsafe.Sizeof(uintptr(0))) + assert.Equal(t, unsafe.Sizeof(StructuredType{}), unsafe.Sizeof(ConstrainedType{})+1*unsafe.Sizeof(uintptr(0))) } func TestStructuredTypeExtra(t *testing.T) { @@ -559,7 +796,7 @@ func TestStructuredTypeExtra(t *testing.T) { func TestCompactTypeNodeLinksLayout(t *testing.T) { t.Parallel() - assert.Equal(t, unsafe.Sizeof(TypeNodeLinks{}), 2*unsafe.Sizeof(uintptr(0))) + assert.Equal(t, unsafe.Sizeof(TypeNodeLinks{}), 3*unsafe.Sizeof(uintptr(0))) c, types := newCompositeTestChecker() var links TypeNodeLinks links.resolvedType = types[0] @@ -591,8 +828,8 @@ func TestCompactTypeNodeLinksLayout(t *testing.T) { func TestCompactSignatureLayout(t *testing.T) { t.Parallel() - assert.Equal(t, unsafe.Sizeof(Signature{}), 16+8*unsafe.Sizeof(uintptr(0))) - assert.Equal(t, unsafe.Sizeof(signatureExtra{}), 7*unsafe.Sizeof(uintptr(0))) + assert.Equal(t, unsafe.Sizeof(Signature{}), 16+9*unsafe.Sizeof(uintptr(0))) + assert.Equal(t, unsafe.Sizeof(signatureExtra{}), 10*unsafe.Sizeof(uintptr(0))) } func TestSignatureExtra(t *testing.T) { diff --git a/tsc/internal/checker/utilities.go b/tsc/internal/checker/utilities.go index 3a7e9993ba879..3417bb203b8d1 100644 --- a/tsc/internal/checker/utilities.go +++ b/tsc/internal/checker/utilities.go @@ -8,7 +8,6 @@ import ( "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/binder" - "github.com/microsoft/TypeScript/tsc/internal/collections" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/debug" "github.com/microsoft/TypeScript/tsc/internal/diagnostics" @@ -255,7 +254,7 @@ func isSyntacticDefault(node *ast.Node) bool { } func hasExportAssignmentSymbol(moduleSymbol *ast.Symbol) bool { - return moduleSymbol.Exports()[ast.InternalSymbolNameExportEquals] != nil + return moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) != nil } func isTypeAlias(node *ast.Node) bool { @@ -352,15 +351,109 @@ func createSymbolTable(symbols []*ast.Symbol) ast.SymbolTable { if len(symbols) == 0 { return nil } - result := make(ast.SymbolTable) + result := ast.NewSymbolTable() for _, symbol := range symbols { - result[symbol.Name()] = symbol + result.Set(symbol.Name(), symbol) } return result } +type symbolSortEntry struct { + key uint64 + symbol *ast.Symbol +} + +type nodeFileIndexEntry struct { + node *ast.Node + file *ast.SourceFile + fileIndex int +} + +func (c *Checker) getNodeFileAndIndex(n *ast.Node) (*ast.SourceFile, int) { + if c.nodeFileIndexCache == nil { + c.nodeFileIndexCache = new([4096]nodeFileIndexEntry) + } + if p := n.Parent; p != nil { + pslot := (uint32(p.Pos())*2654435761 ^ uint32(p.End()) ^ uint32(p.Kind)) & 4095 + pentry := &c.nodeFileIndexCache[pslot] + if pentry.node == p { + return pentry.file, pentry.fileIndex + } + sf := ast.GetSourceFileOfNode(p) + idx := c.fileIndexMap[sf] + *pentry = nodeFileIndexEntry{node: p, file: sf, fileIndex: idx} + return sf, idx + } + slot := (uint32(n.Pos())*2654435761 ^ uint32(n.End()) ^ uint32(n.Kind)) & 4095 + entry := &c.nodeFileIndexCache[slot] + if entry.node == n { + return entry.file, entry.fileIndex + } + sf := ast.GetSourceFileOfNode(n) + idx := c.fileIndexMap[sf] + *entry = nodeFileIndexEntry{node: n, file: sf, fileIndex: idx} + return sf, idx +} + func (c *Checker) sortSymbols(symbols []*ast.Symbol) { - slices.SortFunc(symbols, c.compareSymbols) + n := len(symbols) + if n <= 1 { + return + } + if n == 2 || len(c.files) == 0 { + slices.SortFunc(symbols, c.compareSymbols) + return + } + buf := c.symbolSortBuf + if cap(buf) < n { + buf = make([]symbolSortEntry, n) + } else { + buf = buf[:n] + } + var lastParent *ast.Node + var lastFileIndex uint64 + var prevKey uint64 + alreadySorted := true + for i, s := range symbols { + var key uint64 + if s == nil { + key = ^uint64(0) + } else if d := s.FirstDeclaration(); d != nil { + var fileIndex uint64 + if d.Parent != nil && d.Parent == lastParent { + fileIndex = lastFileIndex + } else { + _, idx := c.getNodeFileAndIndex(d) + fileIndex = uint64(uint32(idx)) + lastParent = d.Parent + lastFileIndex = fileIndex + } + key = (fileIndex << 32) | uint64(uint32(d.Pos())) + } else { + key = uint64(^uint32(0)) << 32 + } + if i > 0 && key <= prevKey { + alreadySorted = false + } + prevKey = key + buf[i] = symbolSortEntry{key: key, symbol: s} + } + if !alreadySorted { + slices.SortFunc(buf, func(a, b symbolSortEntry) int { + if a.key < b.key { + return -1 + } + if a.key > b.key { + return 1 + } + return c.compareSymbolsWorker(a.symbol, b.symbol) + }) + for i, e := range buf { + symbols[i] = e.symbol + } + } + clear(buf) + c.symbolSortBuf = buf[:0] } func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int { @@ -373,13 +466,14 @@ func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int { if s2 == nil { return -1 } - if len(s1.Declarations()) != 0 && len(s2.Declarations()) != 0 { - if r := c.compareNodes(s1.Declarations()[0], s2.Declarations()[0]); r != 0 { + d1, d2 := s1.FirstDeclaration(), s2.FirstDeclaration() + if d1 != nil && d2 != nil { + if r := c.compareNodes(d1, d2); r != 0 { return r } - } else if len(s1.Declarations()) != 0 { + } else if d1 != nil { return -1 - } else if len(s2.Declarations()) != 0 { + } else if d2 != nil { return 1 } if r := strings.Compare(s1.Name(), s2.Name()); r != 0 { @@ -400,14 +494,12 @@ func (c *Checker) compareNodes(n1, n2 *ast.Node) int { if n2 == nil { return -1 } - if n1.Parent != nil && n1.Parent == n2.Parent { + if n1.Parent != nil && n2.Parent != nil && (n1.Parent == n2.Parent || (n1.Parent.Parent != nil && n1.Parent.Parent == n2.Parent.Parent)) { return n1.Pos() - n2.Pos() } - s1 := ast.GetSourceFileOfNode(n1) - s2 := ast.GetSourceFileOfNode(n2) + s1, f1 := c.getNodeFileAndIndex(n1) + s2, f2 := c.getNodeFileAndIndex(n2) if s1 != s2 { - f1 := c.fileIndexMap[s1] - f2 := c.fileIndexMap[s2] // Order by index of file in the containing program return f1 - f2 } @@ -432,11 +524,22 @@ func CompareTypes(t1, t2 *Type) int { if c := getSortOrderFlags(t1) - getSortOrderFlags(t2); c != 0 { return c } + if t1.flags == TypeFlagsStringLiteral && t2.flags == TypeFlagsStringLiteral && t1.alias == nil && t2.alias == nil { + if c := strings.Compare(t1.AsLiteralType().value.(string), t2.AsLiteralType().value.(string)); c != 0 { + return c + } + return int(t1.id) - int(t2.id) + } + if t1.flags&(TypeFlagsEnum|TypeFlagsEnumLiteral) != 0 && t1.alias == nil && t2.alias == nil { + if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { + return c + } + return int(t1.id) - int(t2.id) + } // Order named types by name and, in the case of aliased types, by alias type arguments. if c := compareTypeNames(t1, t2); c != 0 { return c } - // We have unnamed types or types with identical names. Now sort by data specific to the type. switch { case t1.flags&(TypeFlagsAny|TypeFlagsUnknown|TypeFlagsString|TypeFlagsNumber|TypeFlagsBoolean|TypeFlagsBigInt|TypeFlagsESSymbol|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsNever|TypeFlagsNonPrimitive) != 0: // Only distinguished by type IDs, handled below. @@ -445,11 +548,11 @@ func CompareTypes(t1, t2 *Type) int { // Order other unnamed or identically named object types by symbol. if t1.objectFlags&ObjectFlagsInstantiationExpressionType != 0 && t2.objectFlags&ObjectFlagsInstantiationExpressionType != 0 { var declaration1, declaration2 *ast.Node - if t1.symbol != nil && len(t1.symbol.Declarations()) != 0 { - declaration1 = t1.symbol.Declarations()[0] + if t1.symbol != nil { + declaration1 = t1.symbol.FirstDeclaration() } - if t2.symbol != nil && len(t2.symbol.Declarations()) != 0 { - declaration2 = t2.symbol.Declarations()[0] + if t2.symbol != nil { + declaration2 = t2.symbol.FirstDeclaration() } // A single instantiation expression can produce multiple types for union constituents, // so compare their source declarations before comparing the shared expression node. @@ -459,8 +562,10 @@ func CompareTypes(t1, t2 *Type) int { if c := t1.checker.compareNodes(t1.AsInstantiationExpressionType().node, t2.AsInstantiationExpressionType().node); c != 0 { return c } - } else if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { - return c + } else if t1.symbol != t2.symbol { + if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { + return c + } } // When object types have the same or no symbol, order by kind. We order type references before other kinds. if t1.objectFlags&ObjectFlagsReference != 0 && t2.objectFlags&ObjectFlagsReference != 0 { @@ -637,6 +742,9 @@ func compareTypeNames(t1, t2 *Type) int { s1 := getTypeNameSymbol(t1) s2 := getTypeNameSymbol(t2) if s1 == s2 { + if t1.alias == t2.alias { + return 0 + } return compareTypeLists(t1.alias.TypeArguments(), t2.alias.TypeArguments()) } if s1 == nil { @@ -743,10 +851,10 @@ func compareTypeMappers(m1, m2 *TypeMapper) int { case TypeMapperKindArray: m1 := castTypeMapper[ArrayTypeMapper](m1, TypeMapperKindArray) m2 := castTypeMapper[ArrayTypeMapper](m2, TypeMapperKindArray) - if c := compareTypeLists(m1.sources, m2.sources); c != 0 { + if c := compareTypeLists(m1.sources(), m2.sources()); c != 0 { return c } - return compareTypeLists(m1.targets, m2.targets) + return compareTypeLists(m1.targets(), m2.targets()) case TypeMapperKindMerged: m1 := castTypeMapper[MergedTypeMapper](m1, TypeMapperKindMerged) m2 := castTypeMapper[MergedTypeMapper](m2, TypeMapperKindMerged) @@ -921,11 +1029,14 @@ func (c *Checker) releaseTemporaryTypeSet(set *temporaryTypeSet) { if cap(set.values) > len(set.inline) { clear(set.inline[:]) } - clear(set.valuesByKey) + if len(set.valuesByKey) > 256 || cap(set.values) > 4096 { + set.valuesByKey = nil + } else if len(set.valuesByKey) != 0 { + clear(set.valuesByKey) + } // Do not retain arbitrarily large temporary unions and their dedup maps. if cap(set.values) > 4096 { set.values = set.inline[:0] - set.valuesByKey = nil } else { set.values = set.values[:0] } @@ -933,9 +1044,50 @@ func (c *Checker) releaseTemporaryTypeSet(set *temporaryTypeSet) { c.freeTypeSets = set } +// temporaryPropertyCounts counts property names in first-occurrence order. type temporaryPropertyCounts struct { - collections.OrderedMap[string, int] - next *temporaryPropertyCounts + entries []propertyCount + indices map[string]int32 + next *temporaryPropertyCounts +} + +type propertyCount struct { + name string + count int32 +} + +func (p *temporaryPropertyCounts) Add(name string) { + if i, ok := p.indices[name]; ok { + p.entries[i].count++ + return + } + p.AddNew(name) +} + +// AddNew adds a name known not to have been added yet, skipping the lookup. +func (p *temporaryPropertyCounts) AddNew(name string) { + if p.indices == nil { + p.indices = make(map[string]int32) + } + p.indices[name] = int32(len(p.entries)) + p.entries = append(p.entries, propertyCount{name: name, count: 1}) +} + +func (p *temporaryPropertyCounts) Count(name string) int { + if i, ok := p.indices[name]; ok { + return int(p.entries[i].count) + } + return 0 +} + +func (p *temporaryPropertyCounts) Size() int { + return len(p.entries) +} + +func (p *temporaryPropertyCounts) Clear() { + clear(p.entries) + p.entries = p.entries[:0] + clear(p.indices) } func (c *Checker) getTemporaryPropertyCounts() *temporaryPropertyCounts { @@ -960,8 +1112,30 @@ func (c *Checker) releaseTemporaryPropertyCounts(counts *temporaryPropertyCounts c.freePropertyCounts = counts } +// Property enumeration of a combined type may recursively enumerate other +// combined types, so each call borrows a separate name set. +func (c *Checker) getTemporaryNameSet() map[string]struct{} { + if n := len(c.freeNameSets); n != 0 { + set := c.freeNameSets[n-1] + c.freeNameSets = c.freeNameSets[:n-1] + return set + } + return make(map[string]struct{}) +} + +func (c *Checker) releaseTemporaryNameSet(set map[string]struct{}) { + // Do not retain unusually large tables in the free list. + if len(set) > 256 { + return + } + if len(set) != 0 { + clear(set) + } + c.freeNameSets = append(c.freeNameSets, set) +} + func (s *orderedSet[T]) contains(value T) bool { - if s.valuesByKey == nil { + if len(s.values) <= orderedSetMapThreshold { return slices.Contains(s.values, value) } _, ok := s.valuesByKey[value] @@ -972,11 +1146,13 @@ func (s *orderedSet[T]) add(value T) { s.values = append(s.values, value) // Small sets are served by a linear scan over values; only materialize the map once the set // grows large enough for hashing to win. - if s.valuesByKey == nil { - if len(s.values) <= orderedSetMapThreshold { - return + if len(s.values) <= orderedSetMapThreshold { + return + } + if len(s.values) == orderedSetMapThreshold+1 { + if s.valuesByKey == nil { + s.valuesByKey = make(map[T]struct{}, len(s.values)) } - s.valuesByKey = make(map[T]struct{}, len(s.values)) for _, v := range s.values[:len(s.values)-1] { s.valuesByKey[v] = struct{}{} } @@ -1274,7 +1450,7 @@ func isInNameOfExpressionWithTypeArgumentsOrHeritageTypeReference(node *ast.Node } func getIndexSymbolFromSymbolTable(symbolTable ast.SymbolTable) *ast.Symbol { - return symbolTable[ast.InternalSymbolNameIndex] + return symbolTable.Get(ast.InternalSymbolNameIndex) } // Indicates whether the result of an `Expression` will be unused. @@ -1768,7 +1944,7 @@ func introducesArgumentsExoticObject(node *ast.Node) bool { func symbolsToArray(symbols ast.SymbolTable) []*ast.Symbol { var result []*ast.Symbol - for id, symbol := range symbols { + for id, symbol := range symbols.All() { if !isReservedMemberName(id) { result = append(result, symbol) } diff --git a/tsc/internal/collections/syncmap.go b/tsc/internal/collections/syncmap.go index c191430535c9d..01e3b1af2345a 100644 --- a/tsc/internal/collections/syncmap.go +++ b/tsc/internal/collections/syncmap.go @@ -1,3 +1,5 @@ +//go:build !wasm + package collections import ( diff --git a/tsc/internal/collections/syncmap_wasm.go b/tsc/internal/collections/syncmap_wasm.go new file mode 100644 index 0000000000000..9daf55935c156 --- /dev/null +++ b/tsc/internal/collections/syncmap_wasm.go @@ -0,0 +1,87 @@ +//go:build wasm + +package collections + +import ( + "iter" + "maps" +) + +type SyncMap[K comparable, V any] struct { + _ [0]K + _ [0]V + m map[K]V +} + +func (s *SyncMap[K, V]) Load(key K) (value V, ok bool) { + if s.m == nil { + return value, false + } + val, ok := s.m[key] + return val, ok +} + +func (s *SyncMap[K, V]) Store(key K, value V) { + if s.m == nil { + s.m = make(map[K]V) + } + s.m[key] = value +} + +func (s *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { + if s.m == nil { + s.m = make(map[K]V) + } else if existing, ok := s.m[key]; ok { + return existing, true + } + s.m[key] = value + return value, false +} + +func (s *SyncMap[K, V]) Delete(key K) { + if s.m != nil { + delete(s.m, key) + } +} + +func (s *SyncMap[K, V]) Clear() { + if s.m != nil { + clear(s.m) + } +} + +func (s *SyncMap[K, V]) Range(f func(key K, value V) bool) { + for k, v := range s.m { + if !f(k, v) { + break + } + } +} + +func (s *SyncMap[K, V]) Size() int { + return len(s.m) +} + +func (s *SyncMap[K, V]) ToMap() map[K]V { + if s.m == nil { + return make(map[K]V) + } + return maps.Clone(s.m) +} + +func (s *SyncMap[K, V]) Keys() iter.Seq[K] { + return func(yield func(K) bool) { + for k := range s.m { + if !yield(k) { + return + } + } + } +} + +func (s *SyncMap[K, V]) Clone() *SyncMap[K, V] { + if s.m == nil { + return &SyncMap[K, V]{} + } + return &SyncMap[K, V]{m: maps.Clone(s.m)} +} diff --git a/tsc/internal/compiler/checkerpool.go b/tsc/internal/compiler/checkerpool.go index e129951e423df..36ec1e5825172 100644 --- a/tsc/internal/compiler/checkerpool.go +++ b/tsc/internal/compiler/checkerpool.go @@ -3,9 +3,11 @@ package compiler import ( "context" "math" + "runtime" "slices" "sort" "sync" + "sync/atomic" "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/checker" @@ -32,7 +34,16 @@ type checkerPool struct { createCheckersOnce sync.Once checkers []*checker.Checker locks []*sync.Mutex - fileAssociations map[*ast.SourceFile]*checker.Checker + fileAssociations map[*ast.SourceFile]int + + maxMemoryBytes uint64 + recycleMu sync.Mutex + retiredGlobalDiagnostics []*ast.Diagnostic + retiredSymbolCount atomic.Uint64 + retiredTypeCount atomic.Uint64 + retiredInstantiationCount atomic.Uint64 + retiredCheckerCount atomic.Uint64 + readMemoryAllocationInBytes func() uint64 } var _ CheckerPool = (*checkerPool)(nil) @@ -321,6 +332,21 @@ func newCheckerPoolWithTracing(program *Program, tr *tracing.Tracing) *checkerPo checkers: make([]*checker.Checker, checkerCount), locks: make([]*sync.Mutex, checkerCount), tracing: tr, + readMemoryAllocationInBytes: func() uint64 { + var stats runtime.MemStats + runtime.ReadMemStats(&stats) + return stats.Alloc + }, + } + options := program.Options() + if maxMemory := options.MaxMemory; maxMemory != nil && + *maxMemory > 0 && + options.NoEmit.IsTrue() && + !options.GetEmitDeclarations() && + !options.IsIncremental() && + options.GenerateTrace == "" && + options.Watch.IsFalseOrUnknown() { + pool.maxMemoryBytes = uint64(*maxMemory) * 1024 * 1024 } return pool @@ -345,14 +371,14 @@ func (p *checkerPool) GetChecker(ctx context.Context, file *ast.SourceFile) (*ch // e.g. for read-only operations like obtaining an emit resolver. func (p *checkerPool) getCheckerForFileNonExclusive(file *ast.SourceFile) (*checker.Checker, func()) { p.createCheckers() - return p.fileAssociations[file], noop + return p.checkers[p.fileAssociations[file]], noop } func (p *checkerPool) getCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { p.createCheckers() - c := p.fileAssociations[file] - idx := slices.Index(p.checkers, c) + idx := p.fileAssociations[file] p.locks[idx].Lock() + c := p.checkers[idx] return c, sync.OnceFunc(func() { p.locks[idx].Unlock() }) @@ -412,9 +438,9 @@ func (p *checkerPool) createCheckers() { fileOrder := getCheckerAssociationOrder(fileWeights, isDeclarationFile, policy.prioritizeSourceFiles) associations = getCheckerAssociationsInOrder(fileWeights, adjacentFiles, fileOrder, checkerCount, policy.balancePenaltyMultiplier) } - p.fileAssociations = make(map[*ast.SourceFile]*checker.Checker, len(p.program.files)) + p.fileAssociations = make(map[*ast.SourceFile]int, len(p.program.files)) for i, file := range p.program.files { - p.fileAssociations[file] = p.checkers[associations[i]] + p.fileAssociations[file] = associations[i] } }) } @@ -451,11 +477,11 @@ func (p *checkerPool) getImportAdjacency() [][]int { func (p *checkerPool) forEachCheckerParallel(cb func(idx int, c *checker.Checker)) { p.createCheckers() wg := core.NewWorkGroup(p.program.SingleThreaded()) - for idx, checker := range p.checkers { + for idx := range p.checkers { wg.Queue(func() { p.locks[idx].Lock() defer p.locks[idx].Unlock() - cb(idx, checker) + cb(idx, p.checkers[idx]) }) } wg.RunAndWait() @@ -467,24 +493,175 @@ func (p *checkerPool) GetGlobalDiagnostics() []*ast.Diagnostic { p.forEachCheckerParallel(func(idx int, checker *checker.Checker) { globalDiagnostics[idx] = checker.GetGlobalDiagnostics() }) - return SortAndDeduplicateDiagnostics(slices.Concat(globalDiagnostics...)) + p.recycleMu.Lock() + retired := slices.Clone(p.retiredGlobalDiagnostics) + p.recycleMu.Unlock() + return SortAndDeduplicateDiagnostics(append(slices.Concat(globalDiagnostics...), retired...)) } // forEachCheckerGroupDo runs one task per checker in parallel. Each task iterates // the provided files, processing only those assigned to its checker. Within each // checker's set, files are visited in their original order. +type checkerWorkQueue struct { + items []int + state atomic.Uint64 // low 32 bits: head, high 32 bits: tail +} + +func (q *checkerWorkQueue) popFront() (int, bool) { + for { + s := q.state.Load() + head := uint32(s) + tail := uint32(s >> 32) + if head >= tail { + return 0, false + } + if q.state.CompareAndSwap(s, uint64(head+1)|(uint64(tail)<<32)) { + return q.items[head], true + } + } +} + +func (q *checkerWorkQueue) popBack() (int, bool) { + for { + s := q.state.Load() + head := uint32(s) + tail := uint32(s >> 32) + if head >= tail { + return 0, false + } + newTail := tail - 1 + if q.state.CompareAndSwap(s, uint64(head)|(uint64(newTail)<<32)) { + return q.items[newTail], true + } + } +} + +func (q *checkerWorkQueue) remaining() uint32 { + s := q.state.Load() + head := uint32(s) + tail := uint32(s >> 32) + if head >= tail { + return 0 + } + return tail - head +} + func (p *checkerPool) forEachCheckerGroupDo(ctx context.Context, files []*ast.SourceFile, singleThreaded bool, cb func(c *checker.Checker, fileIndex int, file *ast.SourceFile)) { p.createCheckers() - checkerCount := len(p.checkers) + if checkerCount == 1 { + if prev := runtime.GOMAXPROCS(0); prev > 1 { + runtime.GOMAXPROCS(1) + defer runtime.GOMAXPROCS(prev) + } + p.locks[0].Lock() + defer p.locks[0].Unlock() + current := p.checkers[0] + if p.maxMemoryBytes == 0 { + for i, file := range files { + cb(current, i, file) + } + return + } + remaining := 0 + for _, file := range files { + if !p.program.SkipTypeChecking(file, false) { + remaining++ + } + } + for i, file := range files { + cb(current, i, file) + if p.program.SkipTypeChecking(file, false) { + continue + } + remaining-- + if remaining != 0 { + current = p.recycleCheckerIfOverBudget(0, current) + } + } + return + } + if !singleThreaded && p.maxMemoryBytes == 0 { + if prev := runtime.GOMAXPROCS(0); prev > checkerCount { + runtime.GOMAXPROCS(checkerCount) + defer runtime.GOMAXPROCS(prev) + } + queues := make([]checkerWorkQueue, checkerCount) + counts := make([]int, checkerCount) + fileCheckers := make([]int, len(files)) + for i, file := range files { + cIdx := p.fileAssociations[file] + fileCheckers[i] = cIdx + counts[cIdx]++ + } + allItems := make([]int, len(files)) + offset := 0 + for cIdx, cnt := range counts { + queues[cIdx].items = allItems[offset : offset : offset+cnt] + queues[cIdx].state.Store(uint64(cnt) << 32) + offset += cnt + } + for i, cIdx := range fileCheckers { + queues[cIdx].items = append(queues[cIdx].items, i) + } + wg := core.NewWorkGroup(false) + for checkerIdx := range checkerCount { + wg.Queue(func() { + p.locks[checkerIdx].Lock() + defer p.locks[checkerIdx].Unlock() + current := p.checkers[checkerIdx] + for { + if i, ok := queues[checkerIdx].popFront(); ok { + cb(current, i, files[i]) + continue + } + bestVictim := -1 + bestRem := uint32(0) + for v := range checkerCount { + if v != checkerIdx { + if rem := queues[v].remaining(); rem > bestRem { + bestRem = rem + bestVictim = v + } + } + } + if bestVictim < 0 { + break + } + if i, ok := queues[bestVictim].popBack(); ok { + p.recycleMu.Lock() + p.fileAssociations[files[i]] = checkerIdx + p.recycleMu.Unlock() + cb(current, i, files[i]) + } + } + }) + } + wg.RunAndWait() + return + } + remainingCheckable := make([]int, checkerCount) + for _, file := range files { + if !p.program.SkipTypeChecking(file, false) { + remainingCheckable[p.fileAssociations[file]]++ + } + } wg := core.NewWorkGroup(singleThreaded) for checkerIdx := range checkerCount { wg.Queue(func() { p.locks[checkerIdx].Lock() defer p.locks[checkerIdx].Unlock() + current := p.checkers[checkerIdx] for i, file := range files { - if checker := p.checkers[checkerIdx]; checker == p.fileAssociations[file] { - cb(checker, i, file) + if checkerIdx == p.fileAssociations[file] { + cb(current, i, file) + if p.program.SkipTypeChecking(file, false) { + continue + } + remainingCheckable[checkerIdx]-- + if remainingCheckable[checkerIdx] != 0 { + current = p.recycleCheckerIfOverBudget(checkerIdx, current) + } } } }) @@ -492,4 +669,28 @@ func (p *checkerPool) forEachCheckerGroupDo(ctx context.Context, files []*ast.So wg.RunAndWait() } +func (p *checkerPool) recycleCheckerIfOverBudget(checkerIdx int, current *checker.Checker) *checker.Checker { + if p.maxMemoryBytes == 0 || p.readMemoryAllocationInBytes() <= p.maxMemoryBytes { + return current + } + + p.recycleMu.Lock() + defer p.recycleMu.Unlock() + if p.readMemoryAllocationInBytes() <= p.maxMemoryBytes { + return current + } + + p.retiredGlobalDiagnostics = append(p.retiredGlobalDiagnostics, current.GetGlobalDiagnostics()...) + p.retiredSymbolCount.Add(uint64(current.SymbolCount)) + p.retiredTypeCount.Add(uint64(current.TypeCount)) + p.retiredInstantiationCount.Add(uint64(current.TotalInstantiationCount)) + p.retiredCheckerCount.Add(1) + + replacement, _ := checker.NewChecker(p.program, nil) + p.checkers[checkerIdx] = replacement + current = nil + runtime.GC() + return replacement +} + func noop() {} diff --git a/tsc/internal/compiler/checkerpool_test.go b/tsc/internal/compiler/checkerpool_test.go index 213da456891e9..bc8d3cbec84c9 100644 --- a/tsc/internal/compiler/checkerpool_test.go +++ b/tsc/internal/compiler/checkerpool_test.go @@ -1,12 +1,16 @@ package compiler import ( + "context" "slices" "testing" "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/module" + "github.com/microsoft/TypeScript/tsc/internal/tsoptions" "github.com/microsoft/TypeScript/tsc/internal/tspath" + "github.com/microsoft/TypeScript/tsc/internal/vfs/vfstest" ) func TestGetSourceFileForResolvedModuleUsesResolvedPath(t *testing.T) { @@ -120,6 +124,48 @@ func TestGetCheckerAssociationOrder(t *testing.T) { } } +func TestCheckerRecyclingPreservesDiagnosticsAndStatistics(t *testing.T) { + t.Parallel() + fs := vfstest.FromMap(map[string]any{ + "/tsconfig.json": `{ + "compilerOptions": { "noLib": true, "skipLibCheck": true }, + "files": ["/a.ts", "/skip.d.ts", "/b.ts", "/c.ts"] + }`, + "/a.ts": `const a: string = 1;`, + "/skip.d.ts": `declare const skipped: Missing;`, + "/b.ts": `const b: number = "b";`, + "/c.ts": `const c: boolean = 0;`, + }, tspath.CaseSensitive) + config, configDiagnostics := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", nil, nil, tsoptions.NewDefaultParseConfigHost(fs), nil) + if len(configDiagnostics) != 0 { + t.Fatalf("unexpected config diagnostics: %v", configDiagnostics) + } + config.SetCompilerOptions(config.CompilerOptions().Clone()) + config.CompilerOptions().NoEmit = core.TSTrue + config.CompilerOptions().Checkers = new(1) + config.CompilerOptions().MaxMemory = new(1) + + program := NewProgram(ProgramOptions{ + Config: config, + Host: NewCompilerHost(fs, "/", nil, nil, nil), + }) + pool := program.compilerCheckerPool + pool.readMemoryAllocationInBytes = func() uint64 { + return pool.maxMemoryBytes + 1 + } + + diagnostics := program.GetSemanticDiagnostics(context.Background(), nil) + if len(diagnostics) != 3 { + t.Fatalf("got %d diagnostics, want 3", len(diagnostics)) + } + if got := program.RetiredCheckerCount(); got != 2 { + t.Fatalf("RetiredCheckerCount() = %d, want 2", got) + } + if program.TypeCount() == 0 || program.SymbolCount() == 0 { + t.Fatal("retired checker statistics were not preserved") + } +} + func TestGetCheckerAssociationWeights(t *testing.T) { t.Parallel() diff --git a/tsc/internal/compiler/fileInclude.go b/tsc/internal/compiler/fileInclude.go index e07123ad5e1ca..7a8afdfaa63ed 100644 --- a/tsc/internal/compiler/fileInclude.go +++ b/tsc/internal/compiler/fileInclude.go @@ -30,7 +30,7 @@ type FileIncludeReason struct { kind fileIncludeKind index int isDefaultLib bool - referencedFile *referencedFileData + referencedFile referencedFileData automaticTypeDirective *automaticTypeDirectiveFileData canonicalSourceFile tspath.PathKey } @@ -87,7 +87,7 @@ func (r *FileIncludeReason) isReferencedFile() bool { } func (r *FileIncludeReason) asReferencedFileData() *referencedFileData { - return r.referencedFile + return &r.referencedFile } func (r *FileIncludeReason) asAutomaticTypeDirectiveFileData() *automaticTypeDirectiveFileData { diff --git a/tsc/internal/compiler/fileloader.go b/tsc/internal/compiler/fileloader.go index b8bb4b7283cb4..58a88b2e3fb48 100644 --- a/tsc/internal/compiler/fileloader.go +++ b/tsc/internal/compiler/fileloader.go @@ -59,7 +59,7 @@ type fileLoader struct { contentMapperExtensions []string filesParser *filesParser - rootTasks []*parseTask + rootTasks []parseTask totalFileCount atomic.Int32 libFileCount atomic.Int32 @@ -186,7 +186,7 @@ func processAllProgramFiles( wg: core.NewWorkGroup(singleThreaded), maxDepth: maxNodeModuleJsDepth, }, - rootTasks: make([]*parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)), + rootTasks: make([]parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)+1), supportedExtensions: supportedExtensions, supportedExtensionsWithJsonIfResolveJsonModule: supportedExtensionsWithJsonIfResolveJsonModule, contentMapperExtensions: opts.Config.ContentMapperExtensions(), @@ -208,19 +208,19 @@ func processAllProgramFiles( defer opts.Tracing.Push(tracing.PhaseProgram, "processRootFiles", map[string]any{"count": len(rootFiles)}, false)() } for index, rootFile := range rootFiles { - loader.addRootFileTask(rootFile, opts.Config.RootFileNameForDiagnostic(index), nil, &FileIncludeReason{kind: fileIncludeKindRootFile, index: index}) + loader.addRootFileTask(rootFile, opts.Config.RootFileNameForDiagnostic(index), nil, FileIncludeReason{kind: fileIncludeKindRootFile, index: index}) } if len(rootFiles) > 0 && compilerOptions.NoLib.IsFalseOrUnknown() { if compilerOptions.Lib == nil { name := tsoptions.GetDefaultLibFileName(compilerOptions) libFile := loader.pathForLibFile(name) - loader.addRootTask(libFile.path, libFile, &FileIncludeReason{kind: fileIncludeKindLibFile, isDefaultLib: true}) + loader.addRootTask(libFile.path, libFile, FileIncludeReason{kind: fileIncludeKindLibFile, isDefaultLib: true}) } else { for index, lib := range compilerOptions.Lib { if name, ok := tsoptions.GetLibFileName(lib); ok { libFile := loader.pathForLibFile(name) - loader.addRootTask(libFile.path, libFile, &FileIncludeReason{kind: fileIncludeKindLibFile, index: index}) + loader.addRootTask(libFile.path, libFile, FileIncludeReason{kind: fileIncludeKindLibFile, index: index}) } // !!! error on unknown name } @@ -240,9 +240,9 @@ func (p *fileLoader) toPath(file tspath.RootedPath) tspath.PathKey { return p.caseSensitivity.PathKey(file) } -func (p *fileLoader) addRootTask(fileName tspath.RootedFilePath, libFile *LibFile, includeReason *FileIncludeReason) { +func (p *fileLoader) addRootTask(fileName tspath.RootedFilePath, libFile *LibFile, includeReason FileIncludeReason) { if p.opts.Config.CompilerOptions().AllowNonTsExtensions.IsTrue() || fileName.HasExtension() { - p.rootTasks = append(p.rootTasks, &parseTask{ + p.rootTasks = append(p.rootTasks, parseTask{ normalizedFilePath: fileName, path: p.caseSensitivity.PathKey(tspath.RootedPath(fileName)), libFile: libFile, @@ -251,36 +251,37 @@ func (p *fileLoader) addRootTask(fileName tspath.RootedFilePath, libFile *LibFil } } -func (p *fileLoader) addRootFileTask(fileName tspath.RootedFilePath, referenceText string, libFile *LibFile, includeReason *FileIncludeReason) { +func (p *fileLoader) addRootFileTask(fileName tspath.RootedFilePath, referenceText string, libFile *LibFile, includeReason FileIncludeReason) { resolvedFile, resolvedPath, diagnostic := p.getSourceFileFromReference(fileName, referenceText) - rootTask := &parseTask{ + p.rootTasks = append(p.rootTasks, parseTask{ normalizedFilePath: resolvedFile, path: resolvedPath, libFile: libFile, includeReason: includeReason, - } + }) if diagnostic != nil { + rootTask := &p.rootTasks[len(p.rootTasks)-1] rootTask.normalizedFilePath = fileName rootTask.path = p.caseSensitivity.PathKey(tspath.RootedPath(fileName)) rootTask.failedLookup = true rootTask.processingDiagnostics = []*processingDiagnostic{{ kind: processingDiagnosticKindExplainingFileInclude, explanation: &includeExplainingDiagnostic{ - diagnosticReason: includeReason, + diagnosticReason: &rootTask.includeReason, message: diagnostic.message, args: diagnostic.args, }, }} } - p.rootTasks = append(p.rootTasks, rootTask) } func (p *fileLoader) addAutomaticTypeDirectiveTasks() { containingFileName := p.opts.Config.BaseDirectory().ResolveFile(module.InferredTypesContainingFile) - p.rootTasks = append(p.rootTasks, &parseTask{ + p.rootTasks = append(p.rootTasks, parseTask{ normalizedFilePath: containingFileName, path: p.caseSensitivity.PathKey(tspath.RootedPath(containingFileName)), isForAutomaticTypeDirective: true, + elideOnDepth: false, }) } @@ -311,11 +312,10 @@ func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName tspath.Ro path: resolved.ResolvedPath, increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, - includeReason: &FileIncludeReason{ + includeReason: FileIncludeReason{ kind: fileIncludeKindAutomaticTypeDirectiveFile, automaticTypeDirective: &automaticTypeDirectiveFileData{name, resolved.PackageId}, }, - packageId: resolved.PackageId, }) } else { pDiagnostics = append(pDiagnostics, &processingDiagnostic{ @@ -725,12 +725,12 @@ func (p *fileLoader) getSourceFileFromReference( return "", "", &sourceFileFromReferenceDiagnostic{message: diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, args: []string{diagnosticFileName, "'" + strings.Join(core.Flatten(p.supportedExtensions), "', '") + "'"}} } -func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile tspath.RootedFilePath, index int) (*resolvedRef, *processingDiagnostic) { +func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile tspath.RootedFilePath, index int) (resolvedRef, *processingDiagnostic) { normalizedFileName := containingFile.Directory().ResolveFile(moduleName) containingPath := p.caseSensitivity.PathKey(tspath.RootedPath(containingFile)) - includeReason := &FileIncludeReason{ + includeReason := FileIncludeReason{ kind: fileIncludeKindReferenceFile, - referencedFile: &referencedFileData{ + referencedFile: referencedFileData{ file: containingPath, index: index, }, @@ -741,26 +741,26 @@ func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containi moduleName, ) if diagnostic != nil { - return nil, &processingDiagnostic{ + return resolvedRef{}, &processingDiagnostic{ kind: processingDiagnosticKindExplainingFileInclude, explanation: &includeExplainingDiagnostic{ - diagnosticReason: includeReason, + diagnosticReason: &includeReason, message: diagnostic.message, args: diagnostic.args, }, } } if containingPath == resolvedPath { - return nil, &processingDiagnostic{ + return resolvedRef{}, &processingDiagnostic{ kind: processingDiagnosticKindExplainingFileInclude, explanation: &includeExplainingDiagnostic{ - diagnosticReason: includeReason, + diagnosticReason: &includeReason, message: diagnostics.A_file_cannot_have_a_reference_to_itself, }, } } - return &resolvedRef{ + return resolvedRef{ fileName: resolvedFileName, path: resolvedPath, includeReason: includeReason, @@ -788,9 +788,9 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { traceDone = p.tracing.Push(tracing.PhaseProgram, "processTypeReferenceDirective", map[string]any{"directive": ref.FileName, "hasResolved": resolved.IsResolved(), "refKind": int(fileIncludeKindTypeReferenceDirective), "refPath": string(t.path)}, false) } typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved - includeReason := &FileIncludeReason{ + includeReason := FileIncludeReason{ kind: fileIncludeKindTypeReferenceDirective, - referencedFile: &referencedFileData{ + referencedFile: referencedFileData{ file: t.path, index: index, }, @@ -806,10 +806,10 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { includeReason: includeReason, packageId: resolved.PackageId, }, nil) - } else { + } else if ref.ResolutionMode == core.ResolutionModeNone { t.processingDiagnostics = append(t.processingDiagnostics, &processingDiagnostic{ kind: processingDiagnosticKindUnknownReference, - reason: includeReason, + reason: &includeReason, }) } if traceDone != nil { @@ -939,9 +939,9 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(t *parseTask) { path: resolvedModule.ResolvedPath, increaseDepth: resolvedModule.IsExternalLibraryImport, elideOnDepth: isJsFileFromNodeModules, - includeReason: &FileIncludeReason{ + includeReason: FileIncludeReason{ kind: fileIncludeKindImport, - referencedFile: &referencedFileData{ + referencedFile: referencedFileData{ file: t.path, index: importIndex, synthetic: core.IfElse(importIndex < 0, entry, nil), diff --git a/tsc/internal/compiler/filesparser.go b/tsc/internal/compiler/filesparser.go index 5947c5fbb3a40..a12fb09d81f48 100644 --- a/tsc/internal/compiler/filesparser.go +++ b/tsc/internal/compiler/filesparser.go @@ -5,6 +5,7 @@ import ( "slices" "strings" "sync" + "sync/atomic" "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/collections" @@ -22,13 +23,13 @@ type parseTask struct { file *ast.SourceFile libFile *LibFile redirectedParseTask *parseTask - subTasks []*parseTask + subTasks []parseTask loaded bool startedSubTasks bool isForAutomaticTypeDirective bool isContentMapperSupplemental bool failedLookup bool - includeReason *FileIncludeReason + includeReason FileIncludeReason packageId module.PackageId metadata ast.SourceFileMetaData @@ -36,7 +37,6 @@ type parseTask struct { resolutionsTrace []module.DiagAndArgs typeResolutionsInFile module.ModeAwareCache[*module.ResolvedTypeReferenceDirective] typeResolutionsTrace []module.DiagAndArgs - resolutionDiagnostics []*ast.Diagnostic processingDiagnostics []*processingDiagnostic importHelpersImportSpecifier *ast.StringLiteralNode jsxRuntimeImportSpecifier *jsxRuntimeImportSpecifier @@ -44,8 +44,7 @@ type parseTask struct { increaseDepth bool elideOnDepth bool - loadedTask *parseTask - allIncludeReasons []*FileIncludeReason + loadedTask *parseTask } func (t *parseTask) FileName() tspath.RootedFilePath { @@ -86,7 +85,7 @@ func (t *parseTask) load(loader *fileLoader) { t.processingDiagnostics = append(t.processingDiagnostics, &processingDiagnostic{ kind: processingDiagnosticKindExplainingFileInclude, explanation: &includeExplainingDiagnostic{ - diagnosticReason: t.includeReason, + diagnosticReason: &t.includeReason, message: diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, args: []string{t.normalizedFilePath.AsString()}, }, @@ -95,7 +94,7 @@ func (t *parseTask) load(loader *fileLoader) { t.processingDiagnostics = append(t.processingDiagnostics, &processingDiagnostic{ kind: processingDiagnosticKindExplainingFileInclude, explanation: &includeExplainingDiagnostic{ - diagnosticReason: t.includeReason, + diagnosticReason: &t.includeReason, message: diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, args: []string{t.normalizedFilePath.AsString(), "'" + strings.Join(core.Flatten(loader.supportedExtensions), "', '") + "'"}, }, @@ -128,7 +127,7 @@ func (t *parseTask) load(loader *fileLoader) { if virtualFileName := file.VirtualFileName(); virtualFileName != "" { t.metadata.ImpliedNodeFormat = ast.GetImpliedNodeFormatForFile(virtualFileName, t.metadata.PackageJsonType) } - t.subTasks = make([]*parseTask, 0, len(file.ReferencedFiles)+len(file.Imports())+len(file.ModuleAugmentations)) + t.subTasks = make([]parseTask, 0, len(file.ReferencedFiles)+len(file.Imports())+len(file.ModuleAugmentations)+len(file.TypeReferenceDirectives)+len(file.LibReferenceDirectives)+len(file.SupplementalSourceFiles())) compilerOptions := loader.opts.Config.CompilerOptions() if !compilerOptions.NoResolve.IsTrue() && !loader.opts.SkipModuleResolution { @@ -138,17 +137,16 @@ func (t *parseTask) load(loader *fileLoader) { t.processingDiagnostics = append(t.processingDiagnostics, processingDiagnostic) continue } - t.addSubTask(*resolvedRef, nil) + t.addSubTask(resolvedRef, nil) } - loader.resolveTypeReferenceDirectives(t) } if compilerOptions.NoLib != core.TSTrue && !loader.opts.SkipModuleResolution { for index, lib := range file.LibReferenceDirectives { - includeReason := &FileIncludeReason{ + includeReason := FileIncludeReason{ kind: fileIncludeKindLibReferenceDirective, - referencedFile: &referencedFileData{ + referencedFile: referencedFileData{ file: t.path, index: index, }, @@ -163,7 +161,7 @@ func (t *parseTask) load(loader *fileLoader) { } else { t.processingDiagnostics = append(t.processingDiagnostics, &processingDiagnostic{ kind: processingDiagnosticKindUnknownReference, - reason: includeReason, + reason: &includeReason, }) } } @@ -171,12 +169,12 @@ func (t *parseTask) load(loader *fileLoader) { loader.resolveImportsAndModuleAugmentations(t) for _, supplemental := range file.SupplementalSourceFiles() { - t.subTasks = append(t.subTasks, &parseTask{ + t.subTasks = append(t.subTasks, parseTask{ normalizedFilePath: supplemental.FileName(), path: loader.caseSensitivity.PathKey(tspath.RootedPath(supplemental.FileName())), file: supplemental, isContentMapperSupplemental: true, - includeReason: &FileIncludeReason{ + includeReason: FileIncludeReason{ kind: fileIncludeKindContentMapperSupplemental, canonicalSourceFile: t.path, }, @@ -185,14 +183,14 @@ func (t *parseTask) load(loader *fileLoader) { } func (t *parseTask) redirect(loader *fileLoader, fileName tspath.RootedFilePath, path tspath.PathKey) { - t.redirectedParseTask = &parseTask{ + t.subTasks = []parseTask{{ normalizedFilePath: fileName, path: path, libFile: t.libFile, includeReason: t.includeReason, - } + }} // increaseDepth and elideOnDepth are not copied to redirects, otherwise their depth would be double counted. - t.subTasks = []*parseTask{t.redirectedParseTask} + t.redirectedParseTask = &t.subTasks[0] } func (t *parseTask) loadAutomaticTypeDirectives(loader *fileLoader) { @@ -203,6 +201,7 @@ func (t *parseTask) loadAutomaticTypeDirectives(loader *fileLoader) { t.typeResolutionsInFile = typeResolutionsInFile t.typeResolutionsTrace = typeResolutionsTrace t.processingDiagnostics = append(t.processingDiagnostics, pDiagnostics...) + t.subTasks = make([]parseTask, 0, len(toParseTypeRefs)) for _, typeResolution := range toParseTypeRefs { t.addSubTask(typeResolution, nil) } @@ -213,12 +212,12 @@ type resolvedRef struct { path tspath.PathKey increaseDepth bool elideOnDepth bool - includeReason *FileIncludeReason + includeReason FileIncludeReason packageId module.PackageId } func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) { - subTask := &parseTask{ + t.subTasks = append(t.subTasks, parseTask{ normalizedFilePath: ref.fileName, path: ref.path, libFile: libFile, @@ -226,8 +225,7 @@ func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) { elideOnDepth: ref.elideOnDepth, includeReason: ref.includeReason, packageId: ref.packageId, - } - t.subTasks = append(t.subTasks, subTask) + }) } type filesParser struct { @@ -244,93 +242,132 @@ var parseTaskDataPool = sync.Pool{ }, } +func resetParseTaskDataPool() { + parseTaskDataPool = sync.Pool{ + New: func() any { + return &parseTaskData{ + tasks: make(map[tspath.RootedFilePath]*parseTask, 1), + } + }, + } +} + func getParseTaskData(task *parseTask) *parseTaskData { td := parseTaskDataPool.Get().(*parseTaskData) + td.firstTask = task td.tasks[task.normalizedFilePath] = task - td.lowestDepth = math.MaxInt + td.packageId = task.packageId + td.hasPackageId.Store(task.packageId.Name != "") + td.lowestDepth.Store(math.MaxInt32) + td.startedSubTasks = false return td } func putParseTaskData(td *parseTaskData) { + td.firstTask = nil + td.packageId = module.PackageId{} + td.hasPackageId.Store(false) + td.startedSubTasks = false clear(td.tasks) parseTaskDataPool.Put(td) } type parseTaskData struct { + firstTask *parseTask // map of tasks by file casing tasks map[tspath.RootedFilePath]*parseTask mu sync.Mutex - lowestDepth int + lowestDepth atomic.Int32 startedSubTasks bool + hasPackageId atomic.Bool packageId module.PackageId } -func (w *filesParser) parse(loader *fileLoader, tasks []*parseTask) { +func (w *filesParser) parse(loader *fileLoader, tasks []parseTask) { w.start(loader, tasks, 0) w.wg.RunAndWait() } -func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int) { - for i, task := range tasks { - if task.path == "" { - panic("parse task must have a path key: " + task.normalizedFilePath.AsString()) +func (w *filesParser) start(loader *fileLoader, tasks []parseTask, currentDepth int) { + for i := range tasks { + task := &tasks[i] + newCurrentDepth := currentDepth + if task.increaseDepth { + newCurrentDepth = currentDepth + 1 + } + elided := false + if task.elideOnDepth && newCurrentDepth > w.maxDepth { + elided = true } - candidate := getParseTaskData(task) - data, loaded := w.taskDataByPath.LoadOrStore(task.path, candidate) - if loaded { - putParseTaskData(candidate) + data, loaded := w.taskDataByPath.Load(task.path) + if !loaded { + candidate := getParseTaskData(task) + data, loaded = w.taskDataByPath.LoadOrStore(task.path, candidate) + if loaded { + putParseTaskData(candidate) + } + } + if loaded && + task.normalizedFilePath == data.firstTask.normalizedFilePath && + (task.packageId.Name == "" || data.hasPackageId.Load()) && + (elided || newCurrentDepth >= int(data.lowestDepth.Load())) { + task.loadedTask = data.firstTask + continue } w.wg.Queue(func() { - data.mu.Lock() - defer data.mu.Unlock() + w.loadTask(loader, data, task, newCurrentDepth, elided, loaded) + }) + } +} - startSubtasks := false - if loaded { - if existingTask, ok := data.tasks[task.normalizedFilePath]; ok { - tasks[i].loadedTask = existingTask - } else { - data.tasks[task.normalizedFilePath] = task - // This is new task for file name - so load subtasks if there was loading for any other casing - startSubtasks = data.startedSubTasks - } - } +func (w *filesParser) loadTask(loader *fileLoader, data *parseTaskData, task *parseTask, currentDepth int, elided bool, loaded bool) { + data.mu.Lock() + defer data.mu.Unlock() - // Propagate packageId to data if we have one and data doesn't yet - if data.packageId.Name == "" && task.packageId.Name != "" { - data.packageId = task.packageId - } + var startSubtasks bool + if loaded { + if existingTask, ok := data.tasks[task.normalizedFilePath]; ok { + task.loadedTask = existingTask + } else { + data.tasks[task.normalizedFilePath] = task + // This is new task for file name - so load subtasks if there was loading for any other casing + startSubtasks = data.startedSubTasks + } + } - currentDepth := core.IfElse(task.increaseDepth, depth+1, depth) - lowered := currentDepth < data.lowestDepth - if lowered { - // If we're seeing this task at a lower depth than before, - // reprocess its subtasks to ensure they are loaded. - data.lowestDepth = currentDepth - startSubtasks = true - data.startedSubTasks = true - } + // Propagate packageId to data if we have one and data doesn't yet + if data.packageId.Name == "" && task.packageId.Name != "" { + data.packageId = task.packageId + data.hasPackageId.Store(true) + } + lowered := !elided && currentDepth < int(data.lowestDepth.Load()) + if lowered { + // If we're seeing this task at a lower depth than before, + // reprocess its subtasks to ensure they are loaded. + data.lowestDepth.Store(int32(currentDepth)) + startSubtasks = true + data.startedSubTasks = true + } - if task.elideOnDepth && currentDepth > w.maxDepth { - return - } + if elided { + return + } - for _, taskByFileName := range data.tasks { - loadSubTasks := startSubtasks - if !taskByFileName.loaded { - taskByFileName.load(loader) - if taskByFileName.redirectedParseTask != nil { - // Always load redirected task - loadSubTasks = true - data.startedSubTasks = true - } - } - if loadSubTasks && (lowered || !taskByFileName.startedSubTasks) { - taskByFileName.startedSubTasks = true - w.start(loader, taskByFileName.subTasks, data.lowestDepth) - } + for _, taskByFileName := range data.tasks { + loadSubTasks := startSubtasks + if !taskByFileName.loaded { + taskByFileName.load(loader) + if taskByFileName.redirectedParseTask != nil { + // Always load redirected task + loadSubTasks = true + data.startedSubTasks = true } - }) + } + if loadSubTasks && (lowered || !taskByFileName.startedSubTasks) { + taskByFileName.startedSubTasks = true + w.start(loader, taskByFileName.subTasks, int(data.lowestDepth.Load())) + } } } @@ -373,8 +410,6 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { redirectTargetsMap = make(map[tspath.PathKey][]tspath.RootedFilePath) packageIdToSourceFile = make(map[module.PackageId]*ast.SourceFile) } - - var collectFiles func(tasks []*parseTask, seen map[*parseTaskData]tspath.RootedFilePath) // recordedDuplicates tracks, per task data, the set of file-name casings that // have already been recorded in duplicateSourceFiles. A file that is reached // from multiple import sites is walked once per site, but each distinct casing @@ -383,9 +418,11 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { // was acquired when the snapshot is disposed, leaving a dangling cache entry that // panics the next time it is referenced. var recordedDuplicates map[*parseTaskData]*collections.Set[tspath.RootedFilePath] - collectFiles = func(tasks []*parseTask, seen map[*parseTaskData]tspath.RootedFilePath) { - for _, task := range tasks { - includeReason := task.includeReason + var collectFiles func(tasks []parseTask, seen map[*parseTaskData]tspath.RootedFilePath) + collectFiles = func(tasks []parseTask, seen map[*parseTaskData]tspath.RootedFilePath) { + for i := range tasks { + task := &tasks[i] + includeReason := &task.includeReason // Exclude automatic type directive tasks from include reason processing, // as these are internal implementation details and should not contribute // to the reasons for including files. @@ -478,7 +515,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { target: packageIdFile.PathKey(), } filesByPath[task.path] = packageIdFile - if data.lowestDepth > 0 { + if data.lowestDepth.Load() > 0 { sourceFilesFoundSearchingNodeModules.Add(task.path) } continue @@ -543,7 +580,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { } importHelpersImportSpecifiers[path] = task.importHelpersImportSpecifier } - if data.lowestDepth > 0 { + if data.lowestDepth.Load() > 0 { sourceFilesFoundSearchingNodeModules.Add(path) } } diff --git a/tsc/internal/compiler/host.go b/tsc/internal/compiler/host.go index 8ff15f75cce38..889de7e179014 100644 --- a/tsc/internal/compiler/host.go +++ b/tsc/internal/compiler/host.go @@ -1,17 +1,82 @@ package compiler import ( + "runtime" + "strings" + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/binder" + "github.com/microsoft/TypeScript/tsc/internal/checker" "github.com/microsoft/TypeScript/tsc/internal/contentmapper" "github.com/microsoft/TypeScript/tsc/internal/core" "github.com/microsoft/TypeScript/tsc/internal/diagnostics" "github.com/microsoft/TypeScript/tsc/internal/parser" + "github.com/microsoft/TypeScript/tsc/internal/printer" "github.com/microsoft/TypeScript/tsc/internal/tsoptions" "github.com/microsoft/TypeScript/tsc/internal/tspath" "github.com/microsoft/TypeScript/tsc/internal/vfs" "github.com/microsoft/TypeScript/tsc/internal/vfs/cachedvfs" ) +type wasmCachedDts struct { + size int64 + mtime int64 + file *ast.SourceFile +} + +var ( + wasmStaticDtsCache map[ast.SourceFileParseOptions]wasmCachedDts + wasmHeapMarked bool + wasmMarkHeapHook func() + wasmResetHeapHook func() +) + +func SetWasmHeapHooks(markFn, resetFn func()) { + wasmMarkHeapHook = markFn + wasmResetHeapHook = resetFn +} + +func isStaticWasmDts(fileName tspath.RootedFilePath) bool { + return !strings.Contains(fileName.AsString(), "state") +} + +func resetAllWasmPools() { + ast.ResetForcedLazyLists() + for _, entry := range wasmStaticDtsCache { + if entry.file != nil { + entry.file.ResetWasmCaches() + } + } + core.ResetWasmPools() + parser.ResetWasmPools() + binder.ResetWasmPools() + printer.ResetWasmPools() + checker.ResetWasmPools() + resetParseTaskDataPool() +} + +func FinishWasmProgramInit(program *Program) { + if runtime.GOARCH != "wasm" || wasmMarkHeapHook == nil { + return + } + if !wasmHeapMarked { + program.BindSourceFiles() + core.NodeCoreModules() + resetAllWasmPools() + wasmMarkHeapHook() + ast.SetTrackForcedLazy(true) + wasmHeapMarked = true + } +} + +func ResetWasmHeapToWatermark() { + if !wasmHeapMarked || wasmResetHeapHook == nil { + return + } + resetAllWasmPools() + wasmResetHeapHook() +} + type CompilerHost interface { FS() vfs.FS DefaultLibraryPath() tspath.RootedDirectoryPath @@ -81,6 +146,39 @@ func (h *compilerHost) Trace(msg *diagnostics.Message, args ...any) { } func (h *compilerHost) GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile { + if runtime.GOARCH == "wasm" && wasmMarkHeapHook != nil && opts.FileName.IsDeclarationFile() && isStaticWasmDts(opts.FileName) { + if wasmStaticDtsCache != nil { + if entry, ok := wasmStaticDtsCache[opts]; ok { + if fi := h.FS().Stat(opts.FileName.AsPath()); fi != nil && fi.Size() == entry.size && fi.ModTime().UnixNano() == entry.mtime { + return entry.file + } + } + } + if !wasmHeapMarked { + text, ok := h.FS().ReadFile(opts.FileName) + if !ok { + return nil + } + sf := parser.ParseSourceFile(opts, text, core.EnsureScriptKindFromFileName(opts.FileName)) + binder.BindSourceFile(sf) + if wasmStaticDtsCache == nil { + wasmStaticDtsCache = make(map[ast.SourceFileParseOptions]wasmCachedDts, 64) + } + var size, mtime int64 + if fi := h.FS().Stat(opts.FileName.AsPath()); fi != nil { + size = fi.Size() + mtime = fi.ModTime().UnixNano() + } else { + size = int64(len(text)) + } + wasmStaticDtsCache[opts] = wasmCachedDts{ + size: size, + mtime: mtime, + file: sf, + } + return sf + } + } text, ok := h.FS().ReadFile(opts.FileName) if !ok { return nil diff --git a/tsc/internal/compiler/program.go b/tsc/internal/compiler/program.go index 9946362def3ad..6fabd5c08e143 100644 --- a/tsc/internal/compiler/program.go +++ b/tsc/internal/compiler/program.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "maps" + "runtime" "slices" "strings" "sync" @@ -597,6 +598,9 @@ func (p *Program) extractUnresolvedImportsFromSourceFile(file *ast.SourceFile) [ } func (p *Program) SingleThreaded() bool { + if runtime.GOARCH == "wasm" { + return true + } return p.opts.SingleThreaded.DefaultIfUnknown(p.Options().SingleThreaded).IsTrue() } @@ -1753,6 +1757,9 @@ func (p *Program) SymbolCount() int { p.ForEachCheckerParallel(func(_ int, c *checker.Checker) { val.Add(c.SymbolCount) }) + if p.compilerCheckerPool != nil { + val.Add(uint32(p.compilerCheckerPool.retiredSymbolCount.Load())) + } return int(val.Load()) } @@ -1761,6 +1768,9 @@ func (p *Program) TypeCount() int { p.ForEachCheckerParallel(func(_ int, c *checker.Checker) { val.Add(c.TypeCount) }) + if p.compilerCheckerPool != nil { + val.Add(uint32(p.compilerCheckerPool.retiredTypeCount.Load())) + } return int(val.Load()) } @@ -1769,9 +1779,19 @@ func (p *Program) InstantiationCount() int { p.ForEachCheckerParallel(func(_ int, c *checker.Checker) { val.Add(c.TotalInstantiationCount) }) + if p.compilerCheckerPool != nil { + val.Add(uint32(p.compilerCheckerPool.retiredInstantiationCount.Load())) + } return int(val.Load()) } +func (p *Program) RetiredCheckerCount() int { + if p.compilerCheckerPool == nil { + return 0 + } + return int(p.compilerCheckerPool.retiredCheckerCount.Load()) +} + func (p *Program) Program() *Program { return p } diff --git a/tsc/internal/contentmapper/hostimpl.go b/tsc/internal/contentmapper/hostimpl.go index 0aec1f879280e..ac066791900d2 100644 --- a/tsc/internal/contentmapper/hostimpl.go +++ b/tsc/internal/contentmapper/hostimpl.go @@ -492,71 +492,7 @@ func NewHost(ctx context.Context, spawner Spawner, diagnosticLocale locale.Local func NewHostWithOptions(ctx context.Context, spawner Spawner, diagnosticLocale locale.Locale, options HostOptions) Host { logger := options.Logger timing := &timingCollector{mappers: make(map[string]*mapperTimingCollector)} - return newWithDial(ctx, diagnosticLocale, timing, func(ctx context.Context, mapper *Mapper, diagnosticLocale locale.Locale) (ipc.Conn, io.Closer, PositionEncoding, string, error) { - if len(mapper.Exec) == 0 { - return nil, nil, "", "", fmt.Errorf("content mapper %q declares no command to run", mapper.Package) - } - mapperTiming := timing.mapper(mapper.Identity()) - diagnosticName := mapper.DiagnosticName() - spawnStart := time.Now() - var stderr io.Writer = io.Discard - var stderrLog *stderrLogger - if logger != nil { - stderrLog = &stderrLogger{mapperName: diagnosticName, logger: logger} - stderr = stderrLog - } - rwc, err := spawner.Spawn(mapper.Exec, mapper.PackageDirectory.AsString(), stderr) - mapperTiming.spawn.record(spawnStart) - if err != nil { - return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindProcessStart, MapperName: diagnosticName, Command: mapper.Exec[0], Detail: err.Error()} - } - if stderrLog != nil { - rwc = &loggedProcess{ReadWriteCloser: rwc, stderr: stderrLog} - } - rwc = &closeOnceReadWriteCloser{ReadWriteCloser: rwc} - protocol := ipc.Protocol(ipc.NewJSONRPCProtocol(rwc)) - if logger != nil { - protocol = &loggingProtocol{Protocol: protocol, mapperName: diagnosticName, logger: logger} - } - conn := ipc.NewAsyncConnWithProtocol(rwc, protocol, rejectHandler{}) - go func() { - _ = conn.Run(ctx) - _ = rwc.Close() - }() - initializeCtx, cancel := context.WithTimeout(ctx, initializeTimeout) - initializeStart := mapperTiming.startRequest() - positionEncoding, diagnosticSource, err := handshake(initializeCtx, conn, diagnosticLocale) - mapperTiming.finishRequest(&mapperTiming.initialize, initializeStart) - initializeCtxErr := initializeCtx.Err() - cancel() - if err != nil { - var exitCode int - var exited bool - if exitState, ok := rwc.(processExitState); ok { - exitCode, exited = exitState.ExitCode() - } - _ = rwc.Close() - if initializeError, ok := errors.AsType[*InitializeError](err); ok { - initializeError.MapperName = diagnosticName - return nil, nil, "", "", initializeError - } - if exited { - return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindProcessExit, MapperName: diagnosticName, ExitCode: exitCode} - } - if initializeCtxErr != nil || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindNoResponse, MapperName: diagnosticName, TimeoutSeconds: initializeTimeoutSeconds} - } - return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindRequest, MapperName: diagnosticName, Detail: err.Error()} - } - return conn, rwc, positionEncoding, diagnosticSource, nil - }) -} - -func newWithDial(ctx context.Context, diagnosticLocale locale.Locale, timing *timingCollector, dial dialFunc) *host { - hostCtx, cancel := context.WithCancel(ctx) - h := &host{ctx: hostCtx, cancel: cancel, dial: dial, timing: timing, diagnosticLocale: diagnosticLocale, conns: make(map[string]*mapperConn), projects: make(map[string]*projectEntry), projectLeases: make(map[string]*projectLease)} - h.stop = context.AfterFunc(ctx, func() { _ = h.Close() }) - return h + return newWithDial(ctx, diagnosticLocale, timing, dialMapperProcess(logger, spawner, timing)) } func (h *host) Timings() Timings { diff --git a/tsc/internal/contentmapper/hostimpl_notwasm.go b/tsc/internal/contentmapper/hostimpl_notwasm.go new file mode 100644 index 0000000000000..202e71889504c --- /dev/null +++ b/tsc/internal/contentmapper/hostimpl_notwasm.go @@ -0,0 +1,82 @@ +//go:build !wasm + +package contentmapper + +import ( + "context" + "errors" + "fmt" + "io" + "time" + + "github.com/microsoft/TypeScript/tsc/internal/ipc" + "github.com/microsoft/TypeScript/tsc/internal/locale" +) + +func dialMapperProcess(logger Logger, spawner Spawner, timing *timingCollector) dialFunc { + return func(ctx context.Context, mapper *Mapper, diagnosticLocale locale.Locale) (ipc.Conn, io.Closer, PositionEncoding, string, error) { + if len(mapper.Exec) == 0 { + return nil, nil, "", "", fmt.Errorf("content mapper %q declares no command to run", mapper.Package) + } + mapperTiming := timing.mapper(mapper.Identity()) + diagnosticName := mapper.DiagnosticName() + spawnStart := time.Now() + var stderr io.Writer = io.Discard + var stderrLog *stderrLogger + if logger != nil { + stderrLog = &stderrLogger{mapperName: diagnosticName, logger: logger} + stderr = stderrLog + } + rwc, err := spawner.Spawn(mapper.Exec, mapper.PackageDirectory.AsString(), stderr) + mapperTiming.spawn.record(spawnStart) + if err != nil { + return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindProcessStart, MapperName: diagnosticName, Command: mapper.Exec[0], Detail: err.Error()} + } + if stderrLog != nil { + rwc = &loggedProcess{ReadWriteCloser: rwc, stderr: stderrLog} + } + rwc = &closeOnceReadWriteCloser{ReadWriteCloser: rwc} + protocol := ipc.Protocol(ipc.NewJSONRPCProtocol(rwc)) + if logger != nil { + protocol = &loggingProtocol{Protocol: protocol, mapperName: diagnosticName, logger: logger} + } + conn := ipc.NewAsyncConnWithProtocol(rwc, protocol, rejectHandler{}) + go func() { + _ = conn.Run(ctx) + _ = rwc.Close() + }() + initializeCtx, cancel := context.WithTimeout(ctx, initializeTimeout) + initializeStart := mapperTiming.startRequest() + positionEncoding, diagnosticSource, err := handshake(initializeCtx, conn, diagnosticLocale) + mapperTiming.finishRequest(&mapperTiming.initialize, initializeStart) + initializeCtxErr := initializeCtx.Err() + cancel() + if err != nil { + var exitCode int + var exited bool + if exitState, ok := rwc.(processExitState); ok { + exitCode, exited = exitState.ExitCode() + } + _ = rwc.Close() + if initializeError, ok := errors.AsType[*InitializeError](err); ok { + initializeError.MapperName = diagnosticName + return nil, nil, "", "", initializeError + } + if exited { + return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindProcessExit, MapperName: diagnosticName, ExitCode: exitCode} + } + if initializeCtxErr != nil || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindNoResponse, MapperName: diagnosticName, TimeoutSeconds: initializeTimeoutSeconds} + } + return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindRequest, MapperName: diagnosticName, Detail: err.Error()} + } + return conn, rwc, positionEncoding, diagnosticSource, nil + } +} + +func newWithDial(ctx context.Context, diagnosticLocale locale.Locale, timing *timingCollector, dial dialFunc) *host { + hostCtx, cancel := context.WithCancel(ctx) + h := &host{ctx: hostCtx, cancel: cancel, dial: dial, timing: timing, diagnosticLocale: diagnosticLocale, conns: make(map[string]*mapperConn), projects: make(map[string]*projectEntry), projectLeases: make(map[string]*projectLease)} + h.stop = context.AfterFunc(ctx, func() { _ = h.Close() }) + return h +} diff --git a/tsc/internal/contentmapper/hostimpl_wasm.go b/tsc/internal/contentmapper/hostimpl_wasm.go new file mode 100644 index 0000000000000..9bdb48c89e621 --- /dev/null +++ b/tsc/internal/contentmapper/hostimpl_wasm.go @@ -0,0 +1,35 @@ +//go:build wasm + +package contentmapper + +import ( + "context" + "fmt" + "io" + + "github.com/microsoft/TypeScript/tsc/internal/ipc" + "github.com/microsoft/TypeScript/tsc/internal/locale" +) + +func dialMapperProcess(_ Logger, _ Spawner, _ *timingCollector) dialFunc { + return func(_ context.Context, mapper *Mapper, _ locale.Locale) (ipc.Conn, io.Closer, PositionEncoding, string, error) { + if len(mapper.Exec) == 0 { + return nil, nil, "", "", fmt.Errorf("content mapper %q declares no command to run", mapper.Package) + } + return nil, nil, "", "", &InitializeError{Kind: InitializeErrorKindProcessStart, MapperName: mapper.DiagnosticName(), Command: mapper.Exec[0], Detail: "spawn not supported on wasm"} + } +} + +func newWithDial(ctx context.Context, diagnosticLocale locale.Locale, timing *timingCollector, dial dialFunc) *host { + return &host{ + ctx: ctx, + cancel: func() {}, + stop: func() bool { return false }, + dial: dial, + timing: timing, + diagnosticLocale: diagnosticLocale, + conns: make(map[string]*mapperConn), + projects: make(map[string]*projectEntry), + projectLeases: make(map[string]*projectLease), + } +} diff --git a/tsc/internal/core/arena.go b/tsc/internal/core/arena.go index bbe802464138c..dbc72130f58f2 100644 --- a/tsc/internal/core/arena.go +++ b/tsc/internal/core/arena.go @@ -1,6 +1,7 @@ package core import ( + "runtime" "slices" "unsafe" ) @@ -15,15 +16,27 @@ type Arena[T any] struct { // capacity and unrelated values retained by a surviving pointer into a chunk. func (a *Arena[T]) New() *T { if len(a.data) == cap(a.data) { - nextSize := nextArenaSize[T](len(a.data)) - // Use the same trick as slices.Concat; Grow rounds up to the next size class. - a.data = slices.Grow[[]T](nil, nextSize) + nextSize := nextArenaSize[T](cap(a.data)) + if runtime.GOARCH == "wasm" { + a.data = make([]T, 0, nextSize) + } else { + // Use the same trick as slices.Concat; Grow rounds up to the next size class. + a.data = slices.Grow[[]T](nil, nextSize) + } } index := len(a.data) a.data = a.data[:index+1] return &a.data[index] } +func (a *Arena[T]) Pop(p *T) { + if n := len(a.data); n > 0 && &a.data[n-1] == p { + var zero T + a.data[n-1] = zero + a.data = a.data[:n-1] + } +} + // Allocate a slice of the given size in the arena. If the requested size is beyond the capacity of the arena // and an arena of the next size up still wouldn't fit the slice, make a separate memory allocation for the slice. // Otherwise, grow the arena if necessary and allocate a slice out of it. The length and capacity of the resulting @@ -33,12 +46,16 @@ func (a *Arena[T]) NewSlice(size int) []T { return nil } if len(a.data)+size > cap(a.data) { - nextSize := nextArenaSize[T](len(a.data)) - if size > nextSize { + nextSize := nextArenaSize[T](cap(a.data)) + if size > nextSize || (cap(a.data)-len(a.data) > size && size*2 > nextSize) { return make([]T, size) } - // Use the same trick as slices.Concat; Grow rounds up to the next size class. - a.data = slices.Grow[[]T](nil, nextSize) + if runtime.GOARCH == "wasm" { + a.data = make([]T, 0, nextSize) + } else { + // Use the same trick as slices.Concat; Grow rounds up to the next size class. + a.data = slices.Grow[[]T](nil, nextSize) + } } newLen := len(a.data) + size slice := a.data[len(a.data):newLen:newLen] @@ -62,10 +79,46 @@ func (a *Arena[T]) Clone(t []T) []T { } func nextArenaSize[T any](size int) int { - // Target 512 bytes before allocator size-class rounding, with at least one - // value. A surviving interior pointer retains and scans the whole chunk. - maxSize := max(512/max(int(unsafe.Sizeof(*new(T))), 1), 1) - size = max(size, 1) - size = min(size*2, 256, maxSize) + if runtime.GOARCH == "wasm" { + maxSize := max(4096/max(int(unsafe.Sizeof(*new(T))), 1), 1) + size = max(size, 8) + size = min(size*2, 1024, maxSize) + return size + } + // Target 32 KiB (four Go pages) before allocator size-class rounding, with at + // least one value. Small arenas start at 8 elements and double up to maxSize. + maxSize := max(32768/max(int(unsafe.Sizeof(*new(T))), 1), 1) + size = max(size, 4) + size = min(size*2, 1024, maxSize) return size } + +// IndexedArena keeps values at stable addresses and references them with uint32 +// indexes instead of Go pointers. Index zero is nil. Unlike Arena, it retains all +// blocks so that an index alone keeps its value reachable through the arena. +type IndexedArena[T any] struct { + blocks []*[1 << indexedArenaPageShift]T + size uint32 +} + +const indexedArenaPageShift = 10 + +func (a *IndexedArena[T]) New() (uint32, *T) { + if a.size == ^uint32(0) { + panic("IndexedArena exhausted") + } + index := a.size + if int(index>>indexedArenaPageShift) >= len(a.blocks) { + a.blocks = append(a.blocks, new([1 << indexedArenaPageShift]T)) + } + a.size = index + 1 + return index + 1, &a.blocks[index>>indexedArenaPageShift][index&((1<>indexedArenaPageShift][i&((1<= lowestGoal.Load() { - return // Stop processing if we already found a lower result - } - - // If we have already visited this node, skip it. - if !visited.AddIfAbsent(getKey(j.node)) { - // Note that if we are here, we already visited this node at a - // previous *level*, which means `visit` must have returned false, - // so we don't need to update our result indices. This holds true - // because we deduplicated jobs before queuing the level. - return - } + runJob := func(i int, j *breadthFirstSearchJob[N]) { + if int64(i) >= lowestGoal.Load() { + return // Stop processing if we already found a lower result + } - isResult, stop := visit(j.node) - if isResult { - // We found a result, so we will stop at this level, but an - // earlier job may still find a true result at a lower index. - if stop { - updateMin(&lowestGoal, int64(i)) - return - } - if fallback == nil { - updateMin(&lowestFallback, int64(i)) - } - } + // If we have already visited this node, skip it. + if !visited.AddIfAbsent(getKey(j.node)) { + // Note that if we are here, we already visited this node at a + // previous *level*, which means `visit` must have returned false, + // so we don't need to update our result indices. This holds true + // because we deduplicated jobs before queuing the level. + return + } - if int64(i) >= lowestGoal.Load() { - // If `visit` is expensive, it's likely that by the time we get here, - // a different job has already found a lower index result, so we - // don't even need to collect the next jobs. + isResult, stop := visit(j.node) + if isResult { + // We found a result, so we will stop at this level, but an + // earlier job may still find a true result at a lower index. + if stop { + updateMin(&lowestGoal, int64(i)) return } - // Add the next level jobs - neighborNodes := neighbors(j.node) - if len(neighborNodes) > 0 { - nextJobCount.Add(int64(len(neighborNodes))) - next[i] = Map(neighborNodes, func(child N) *breadthFirstSearchJob[N] { - return &breadthFirstSearchJob[N]{node: child, parent: j} - }) + if fallback == nil { + updateMin(&lowestFallback, int64(i)) } - }(i, j) + } + + if int64(i) >= lowestGoal.Load() { + // If `visit` is expensive, it's likely that by the time we get here, + // a different job has already found a lower index result, so we + // don't even need to collect the next jobs. + return + } + // Add the next level jobs + neighborNodes := neighbors(j.node) + if len(neighborNodes) > 0 { + nextJobCount.Add(int64(len(neighborNodes))) + next[i] = Map(neighborNodes, func(child N) *breadthFirstSearchJob[N] { + return &breadthFirstSearchJob[N]{node: child, parent: j} + }) + } + } + for j := range jobs.Values() { + if runtime.GOARCH == "wasm" { + runJob(i, j) + } else { + wg.Add(1) + go func(i int, j *breadthFirstSearchJob[N]) { + defer wg.Done() + runJob(i, j) + }(i, j) + } i++ } wg.Wait() diff --git a/tsc/internal/core/core.go b/tsc/internal/core/core.go index f5c296b9b5bb0..fba8e32c19dda 100644 --- a/tsc/internal/core/core.go +++ b/tsc/internal/core/core.go @@ -33,10 +33,41 @@ func ApplyDebugStackLimit() { rtdebug.SetMaxStack(n) } +func SuspendGC() func() { + if os.Getenv("GOGC") != "" { //nolint:forbidigo + return nil + } + prev := rtdebug.SetGCPercent(-1) + if prev < 0 { + return nil + } + return func() { + rtdebug.SetGCPercent(prev) + } +} + func Filter[T any](slice []T, f func(T) bool) []T { for i, value := range slice { if !f(value) { - result := slices.Clone(slice[:i]) + if i == 0 { + for i++; i < len(slice); i++ { + value = slice[i] + if f(value) { + result := make([]T, 1, len(slice)-i) + result[0] = value + for i++; i < len(slice); i++ { + value = slice[i] + if f(value) { + result = append(result, value) + } + } + return result + } + } + return slice[:0:0] + } + result := make([]T, i, len(slice)-1) + copy(result, slice[:i]) for i++; i < len(slice); i++ { value = slice[i] if f(value) { @@ -64,7 +95,25 @@ func FilterSeq[T any](slice []T, f func(T) bool) iter.Seq[T] { func FilterIndex[T any](slice []T, f func(T, int, []T) bool) []T { for i, value := range slice { if !f(value, i, slice) { - result := slices.Clone(slice[:i]) + if i == 0 { + for i++; i < len(slice); i++ { + value = slice[i] + if f(value, i, slice) { + result := make([]T, 1, len(slice)-i) + result[0] = value + for i++; i < len(slice); i++ { + value = slice[i] + if f(value, i, slice) { + result = append(result, value) + } + } + return result + } + } + return slice[:0:0] + } + result := make([]T, i, len(slice)-1) + copy(result, slice[:i]) for i++; i < len(slice); i++ { value = slice[i] if f(value, i, slice) { @@ -309,7 +358,10 @@ func Concatenate[T any](s1 []T, s2 []T) []T { if len(s1) == 0 { return s2 } - return slices.Concat(s1, s2) + result := make([]T, len(s1)+len(s2)) + copy(result, s1) + copy(result[len(s1):], s2) + return result } func Splice[T any](s1 []T, start int, deleteCount int, items ...T) []T { @@ -646,6 +698,14 @@ var levenshteinBuffersPool = sync.Pool{ }, } +func ResetWasmPools() { + levenshteinBuffersPool = sync.Pool{ + New: func() any { + return &levenshteinBuffers{} + }, + } +} + func levenshteinWithMax(buffers *levenshteinBuffers, s1 []rune, s2 []rune, maxValue float64) float64 { bufferSize := len(s2) + 1 buffers.previous = slices.Grow(buffers.previous[:0], bufferSize)[:bufferSize] @@ -825,7 +885,8 @@ func Deduplicate[T comparable](slice []T) []T { if len(slice) > 1 { for i, value := range slice { if slices.Contains(slice[:i], value) { - result := slices.Clone(slice[:i]) + result := make([]T, i, len(slice)-1) + copy(result, slice[:i]) for i++; i < len(slice); i++ { value = slice[i] if !slices.Contains(result, value) { diff --git a/tsc/internal/core/linkstore.go b/tsc/internal/core/linkstore.go index 1866a1a2638f2..fdd3aaa0dedd1 100644 --- a/tsc/internal/core/linkstore.go +++ b/tsc/internal/core/linkstore.go @@ -1,9 +1,5 @@ package core -import ( - "slices" -) - // Links store type LinkStore[K comparable, V any] struct { @@ -34,48 +30,120 @@ func (s *LinkStore[K, V]) TryGet(key K) *V { } const ( - LinkPageShift = 8 + LinkPageShift = 6 LinkPageSize = 1 << LinkPageShift LinkPageMask = LinkPageSize - 1 ) // PagedLinkStore implements a sparse-array-like structure for storing elements keyed by dense uint64 keys. -// Elements are allocated in an area, element references are stored in fixed-size pages of 256 entries, and -// an index of pages is maintained in a growable list. +// Elements are allocated in an arena, and compressed indexes are stored in fixed-size pages of 256 entries. +// The pages contain no Go pointers, halving their size on 64-bit hosts and avoiding GC scans of each entry. type PagedLinkStore[V any] struct { - pages []*[LinkPageSize]*V - arena Arena[V] + pages []*[LinkPageSize]uint32 + pageArena Arena[[LinkPageSize]uint32] + arena IndexedArena[V] } func (s *PagedLinkStore[V]) Get(key uint64) *V { + if pageIndex := int(key >> LinkPageShift); pageIndex < len(s.pages) { + if page := s.pages[pageIndex]; page != nil { + if index := page[key&LinkPageMask]; index != 0 { + index-- + return &s.arena.blocks[index>>indexedArenaPageShift][index&((1<> LinkPageShift if int(pageIndex) >= len(s.pages) { - // Grow the length of the list to pageIndex+1 - s.pages = slices.Grow(s.pages, int(pageIndex)-len(s.pages)+1)[:pageIndex+1] + needed := int(pageIndex) + 1 + if needed <= cap(s.pages) { + s.pages = s.pages[:needed] + } else { + newCap := max(needed, cap(s.pages)*2, 8) + next := make([]*[LinkPageSize]uint32, needed, newCap) + copy(next, s.pages) + s.pages = next + } } page := s.pages[pageIndex] if page == nil { - page = new([LinkPageSize]*V) + page = s.pageArena.New() s.pages[pageIndex] = page } - link := page[key&LinkPageMask] - if link == nil { - link = s.arena.New() - page[key&LinkPageMask] = link + index := page[key&LinkPageMask] + if index == 0 { + id, value := s.arena.New() + page[key&LinkPageMask] = id + return value } - return link + return s.arena.Get(index) } func (s *PagedLinkStore[V]) Has(key uint64) bool { - return s.TryGet(key) != nil + if pageIndex := int(key >> LinkPageShift); pageIndex < len(s.pages) { + if page := s.pages[pageIndex]; page != nil { + return page[key&LinkPageMask] != 0 + } + } + return false } func (s *PagedLinkStore[V]) TryGet(key uint64) *V { + if pageIndex := int(key >> LinkPageShift); pageIndex < len(s.pages) { + if page := s.pages[pageIndex]; page != nil { + if index := page[key&LinkPageMask]; index != 0 { + index-- + return &s.arena.blocks[index>>indexedArenaPageShift][index&((1<> LinkPageShift - if int(pageIndex) < len(s.pages) { + if int(pageIndex) >= len(s.pages) { + needed := int(pageIndex) + 1 + if needed <= cap(s.pages) { + s.pages = s.pages[:needed] + } else { + newCap := max(needed, cap(s.pages)*2, 8) + next := make([]*[LinkPageSize]V, needed, newCap) + copy(next, s.pages) + s.pages = next + } + } + page := s.pages[pageIndex] + if page == nil { + page = s.pageArena.New() + s.pages[pageIndex] = page + } + return &page[key&LinkPageMask] +} + +func (s *PagedValueLinkStore[V]) TryGet(key uint64) *V { + if pageIndex := int(key >> LinkPageShift); pageIndex < len(s.pages) { if page := s.pages[pageIndex]; page != nil { - return page[key&LinkPageMask] + return &page[key&LinkPageMask] } } return nil diff --git a/tsc/internal/core/linkstore_test.go b/tsc/internal/core/linkstore_test.go new file mode 100644 index 0000000000000..af785a980a7f8 --- /dev/null +++ b/tsc/internal/core/linkstore_test.go @@ -0,0 +1,145 @@ +package core_test + +import ( + "runtime" + "strconv" + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/core" + "gotest.tools/v3/assert" +) + +func TestLinkStore(t *testing.T) { + t.Parallel() + type value struct { + number int + text *string + } + var store core.LinkStore[int, value] + assert.Assert(t, !store.Has(0)) + assert.Assert(t, store.TryGet(0) == nil) + + values := make([]*value, 4096) + for i := range values { + v := store.Get(i) + assert.Equal(t, *v, value{}) + v.number = i + v.text = new("retained") + values[i] = v + } + runtime.GC() + for i, v := range values { + assert.Assert(t, store.Has(i)) + assert.Equal(t, store.Get(i), v) + assert.Equal(t, store.TryGet(i), v) + assert.Equal(t, v.number, i) + assert.Equal(t, *v.text, "retained") + } + assert.Assert(t, !store.Has(len(values))) + assert.Assert(t, store.TryGet(len(values)) == nil) +} + +func TestPagedLinkStore(t *testing.T) { + t.Parallel() + var store core.PagedLinkStore[int] + keys := []uint64{0, 255, 256, 65535, 16777215} + values := make([]*int, len(keys)) + for _, key := range keys { + assert.Assert(t, !store.Has(key)) + } + for i, key := range keys { + v := store.Get(key) + assert.Equal(t, *v, 0) + *v = i + 1 + values[i] = v + } + runtime.GC() + for i, key := range keys { + assert.Assert(t, store.Has(key)) + assert.Equal(t, store.Get(key), values[i]) + assert.Equal(t, *store.TryGet(key), i+1) + } + assert.Assert(t, !store.Has(1)) + assert.Assert(t, store.TryGet(1) == nil) +} + +func BenchmarkLinkStore(b *testing.B) { + type value struct { + typ *int + flags uint32 + } + b.ReportAllocs() + for b.Loop() { + var store core.LinkStore[int, value] + for i := range 10000 { + store.Get(i).flags = uint32(i) + } + for i := range 10000 { + if store.Get(i).flags != uint32(i) { + b.Fatal("link changed during arena growth") + } + } + + } +} + +func TestIndexedArena(t *testing.T) { + t.Parallel() + type value struct { + number int + text *string + } + var arena core.IndexedArena[value] + assert.Assert(t, arena.Get(0) == nil) + values := make([]*value, 4096) + for i := range values { + index, v := arena.New() + assert.Equal(t, index, uint32(i+1)) + assert.Equal(t, *v, value{}) + v.number = i + v.text = new("retained") + values[i] = v + } + runtime.GC() + for i, v := range values { + assert.Equal(t, arena.Get(uint32(i+1)), v) + assert.Equal(t, v.number, i) + assert.Equal(t, *v.text, "retained") + } +} + +func TestIndexedArenaReachability(t *testing.T) { + t.Parallel() + var arena core.IndexedArena[*string] + for i := range 4096 { + _, value := arena.New() + *value = new(strconv.Itoa(i)) + } + runtime.GC() + for i := range 4096 { + assert.Equal(t, **arena.Get(uint32(i + 1)), strconv.Itoa(i)) + } +} + +func BenchmarkPagedLinkStore(b *testing.B) { + for _, stride := range []uint64{1, 16, 256} { + b.Run(strconv.FormatUint(stride, 10), func(b *testing.B) { + type value struct { + typ *int + flags uint32 + } + b.ReportAllocs() + for b.Loop() { + var store core.PagedLinkStore[value] + for i := range 10000 { + store.Get(uint64(i) * stride).flags = uint32(i) + } + for i := range 10000 { + if store.Get(uint64(i)*stride).flags != uint32(i) { + b.Fatal("link changed during arena growth") + } + } + } + }) + } +} diff --git a/tsc/internal/core/options_generated.go b/tsc/internal/core/options_generated.go index 911b2d6af13d6..04ff45f076a39 100644 --- a/tsc/internal/core/options_generated.go +++ b/tsc/internal/core/options_generated.go @@ -160,6 +160,7 @@ type CompilerOptions struct { SingleThreaded Tristate `json:"singleThreaded,omitzero"` Quiet Tristate `json:"quiet,omitzero"` Checkers *int `json:"checkers,omitzero"` + MaxMemory *int `json:"maxMemory,omitzero"` } // Clone creates a shallow copy of the CompilerOptions. @@ -297,6 +298,7 @@ func (options *CompilerOptions) Clone() *CompilerOptions { SingleThreaded: options.SingleThreaded, Quiet: options.Quiet, Checkers: options.Checkers, + MaxMemory: options.MaxMemory, } } @@ -707,6 +709,9 @@ func (options *CompilerOptions) Equals(other *CompilerOptions) bool { if options.Checkers != other.Checkers && (options.Checkers == nil || other.Checkers == nil || *options.Checkers != *other.Checkers) { return false } + if options.MaxMemory != other.MaxMemory && (options.MaxMemory == nil || other.MaxMemory == nil || *options.MaxMemory != *other.MaxMemory) { + return false + } return true } diff --git a/tsc/internal/core/workgroup.go b/tsc/internal/core/workgroup.go index 73618a1a19fc1..03568da938dc1 100644 --- a/tsc/internal/core/workgroup.go +++ b/tsc/internal/core/workgroup.go @@ -2,6 +2,7 @@ package core import ( "context" + "runtime" "sync" "sync/atomic" @@ -18,7 +19,7 @@ type WorkGroup interface { } func NewWorkGroup(singleThreaded bool) WorkGroup { - if singleThreaded { + if singleThreaded || runtime.GOARCH == "wasm" { return &singleThreadedWorkGroup{} } return ¶llelWorkGroup{} @@ -35,6 +36,10 @@ func (w *parallelWorkGroup) Queue(fn func()) { if w.done.Load() { panic("Queue called after RunAndWait returned") } + if runtime.GOARCH == "wasm" { + fn() + return + } w.wg.Go(func() { fn() @@ -106,6 +111,10 @@ func NewThrottleGroup(ctx context.Context, semaphore chan struct{}) *ThrottleGro // Go runs the given function in a new goroutine, but first acquires a slot from the semaphore. // The semaphore slot is released when the function completes. func (tg *ThrottleGroup) Go(fn func() error) { + if runtime.GOARCH == "wasm" { + _ = fn() + return + } tg.group.Go(func() error { // Acquire semaphore slot - this will block until a slot is available tg.semaphore <- struct{}{} diff --git a/tsc/internal/diagnostics/diagnosticMessages.generated.json b/tsc/internal/diagnostics/diagnosticMessages.generated.json index bf7f327e371c0..1a6196698ee2c 100644 --- a/tsc/internal/diagnostics/diagnosticMessages.generated.json +++ b/tsc/internal/diagnostics/diagnosticMessages.generated.json @@ -1696,6 +1696,7 @@ "Set_the_number_of_projects_to_build_concurrently_6938": "Set the number of projects to build concurrently.", "Deduplicate_packages_with_the_same_name_and_version_6939": "Deduplicate packages with the same name and version.", "Allow_loading_external_content_mapper_plugins_that_execute_code_during_compilation_6940": "Allow loading external content mapper plugins that execute code during compilation.", + "Set_the_memory_limit_in_MiB_for_recycling_checkers_during_no_emit_type_checking_6941": "Set the memory limit in MiB for recycling checkers during no-emit type checking.", "Variable_0_implicitly_has_an_1_type_7005": "Variable '{0}' implicitly has an '{1}' type.", "Parameter_0_implicitly_has_an_1_type_7006": "Parameter '{0}' implicitly has an '{1}' type.", "Member_0_implicitly_has_an_1_type_7008": "Member '{0}' implicitly has an '{1}' type.", diff --git a/tsc/internal/diagnostics/diagnosticMessages.json b/tsc/internal/diagnostics/diagnosticMessages.json index 895ede2e4fa92..2d1e123fc921c 100644 --- a/tsc/internal/diagnostics/diagnosticMessages.json +++ b/tsc/internal/diagnostics/diagnosticMessages.json @@ -6819,6 +6819,10 @@ "category": "Message", "code": 6940 }, + "Set the memory limit in MiB for recycling checkers during no-emit type checking.": { + "category": "Message", + "code": 6941 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/tsc/internal/diagnostics/diagnostics_generated.go b/tsc/internal/diagnostics/diagnostics_generated.go index fbd75c71bbe08..211ac76164e48 100644 --- a/tsc/internal/diagnostics/diagnostics_generated.go +++ b/tsc/internal/diagnostics/diagnostics_generated.go @@ -3396,6 +3396,8 @@ var Deduplicate_packages_with_the_same_name_and_version = &Message{code: 6939, c var Allow_loading_external_content_mapper_plugins_that_execute_code_during_compilation = &Message{code: 6940, category: CategoryMessage, key: "Allow_loading_external_content_mapper_plugins_that_execute_code_during_compilation_6940", text: "Allow loading external content mapper plugins that execute code during compilation."} +var Set_the_memory_limit_in_MiB_for_recycling_checkers_during_no_emit_type_checking = &Message{code: 6941, category: CategoryMessage, key: "Set_the_memory_limit_in_MiB_for_recycling_checkers_during_no_emit_type_checking_6941", text: "Set the memory limit in MiB for recycling checkers during no-emit type checking."} + var Variable_0_implicitly_has_an_1_type = &Message{code: 7005, category: CategoryError, key: "Variable_0_implicitly_has_an_1_type_7005", text: "Variable '{0}' implicitly has an '{1}' type."} var Parameter_0_implicitly_has_an_1_type = &Message{code: 7006, category: CategoryError, key: "Parameter_0_implicitly_has_an_1_type_7006", text: "Parameter '{0}' implicitly has an '{1}' type."} @@ -6160,6 +6162,7 @@ var allMessages = [...]**Message{ &Set_the_number_of_projects_to_build_concurrently, &Deduplicate_packages_with_the_same_name_and_version, &Allow_loading_external_content_mapper_plugins_that_execute_code_during_compilation, + &Set_the_memory_limit_in_MiB_for_recycling_checkers_during_no_emit_type_checking, &Variable_0_implicitly_has_an_1_type, &Parameter_0_implicitly_has_an_1_type, &Member_0_implicitly_has_an_1_type, diff --git a/tsc/internal/diagnostics/loc/cs-CZ.generated.json b/tsc/internal/diagnostics/loc/cs-CZ.generated.json index bf0bc616ba54d..c8c9edf4453e6 100644 --- a/tsc/internal/diagnostics/loc/cs-CZ.generated.json +++ b/tsc/internal/diagnostics/loc/cs-CZ.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Konstruktory odvozených tříd musí obsahovat volání příkazu super.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Není zadaný obsažený soubor a nedá se určit kořenový adresář – přeskakuje se vyhledávání ve složce node_modules.", "Containing_function_is_not_an_arrow_function_95128": "Obsahující funkce není funkcí šipky.", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "Přípona mapování obsahu „{0}“ nesmí obsahovat oddělovače cest.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "Přípona souboru mapovače obsahu '{0}' je integrovaná přípona a mapovač obsahu ji nemůže zaregistrovat.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "Přípona souboru mapovače obsahu '{0}' je registrována více mapovači obsahu.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "Přípona {0} pro mapování obsahu musí začínat tečkou (.).", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "Výstupní přípona mapování obsahu „{0}“ nesmí být prázdná a musí začínat znakem „.“.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Mapování výstupní přípony z „{0}“ na „{1}“ vyžaduje zapnutí „rewriteRelativeImportExtensions“.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Zdroj mapování výstupní přípony „{0}“ není u tohoto nástroje pro mapování obsahu zaregistrovaný.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Doplňkový výstupní soubor mapovače obsahu {0} je v konfliktu s existujícím souborem.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Mapovače obsahu vyžadují zapnutí příznaku příkazového řádku --runExternalCode.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Určete, která metoda se používá k detekci souborů JS ve formátu modulu.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Pokud je povolena možnost „{0}“, nejsou v souborech globálních skriptů povoleny obory názvů. Pokud tento soubor nemá být globálním skriptem, nastavte možnost „moduleDetection“ na hodnotu „force“ nebo přidejte prázdný příkaz „export {}“.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "U parametrů „this“ nelze použít dekoratéry ani modifikátory.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Vnořené konstrukce exportu CommonJS nelze serializovat pro generování deklarací.", + "Network_path_0_is_not_supported_5116": "Síťová cesta {0} není podporovaná.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Žádný základní konstruktor nemá zadaný počet argumentů typu.", "No_constituent_of_type_0_is_callable_2755": "Žádný konstituent typu {0} se nedá zavolat.", "No_constituent_of_type_0_is_constructable_2759": "Žádný konstituent typu {0} se nedá vytvořit.", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Mapovač obsahu neodpověděl na požadavek initialize během {0} sekund.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Mapovač obsahu nevrátil configIdentity, které je povinné, pokud má mapovač obsahu v souboru package.json nastavené \"dynamicConfig\": true.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Balíček mapovače obsahu {0} se nepovedlo najít.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Balíček mapovače obsahu {0} je mimo otevřený pracovní prostor a nebude načten.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Proces mapovače obsahu se nepovedlo spustit nebo inicializovat.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Proces mapovače obsahu skončil před odpovědí na požadavek initialize (ukončovací kód {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Proces mapovače obsahu selhal při zpracování požadavku projektu.", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Typy {0} nejsou mezi těmito typy kompatibilní.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Typy vrácené metodou {0} nejsou mezi těmito typy kompatibilní.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "Hodnota typescript.contentMapper.exec balíčku mapovače obsahu {0} musí být neprázdné pole řetězců.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "Vlastnost „typescript.contentMapper.outputExtensions“ balíčku mapování obsahu „{0}“ obsahuje neplatné mapování z „{1}“ na „{2}“. Přípony nesmí být prázdné a musí začínat znakem „.“.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "Vlastnost „typescript.contentMapper.outputExtensions“ balíčku mapování obsahu „{0}“ musí být objekt s řetězcovými hodnotami.", "The_value_0_cannot_be_used_here_18050": "Hodnota „{0}“ se tady nedá použít.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "Deklarace proměnné příkazu for...in nemůže obsahovat inicializátor.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "Deklarace proměnné příkazu for...of nemůže obsahovat inicializátor.", diff --git a/tsc/internal/diagnostics/loc/cs-CZ.json.gz b/tsc/internal/diagnostics/loc/cs-CZ.json.gz index 6ac304636b4b2..09a8281afa3c0 100644 Binary files a/tsc/internal/diagnostics/loc/cs-CZ.json.gz and b/tsc/internal/diagnostics/loc/cs-CZ.json.gz differ diff --git a/tsc/internal/diagnostics/loc/de-DE.generated.json b/tsc/internal/diagnostics/loc/de-DE.generated.json index 42b3084937d74..6bfcf1b420b06 100644 --- a/tsc/internal/diagnostics/loc/de-DE.generated.json +++ b/tsc/internal/diagnostics/loc/de-DE.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Konstruktoren für abgeleitete Klassen müssen einen Aufruf \"super\" enthalten.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Die enthaltene Datei wird nicht angegeben, und das Stammverzeichnis kann nicht ermittelt werden. Die Suche im Ordner \"node_modules\" wird übersprungen.", "Containing_function_is_not_an_arrow_function_95128": "Die enthaltende Funktion ist keine Pfeilfunktion.", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "Die Inhaltszuordnungserweiterung „{0}“ darf keine Pfadtrennzeichen enthalten.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "Die Dateierweiterung „{0}“ des Inhaltsmappermoduls ist eine integrierte Erweiterung und kann nicht von einem Inhaltsmapper registriert werden.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "Die Dateierweiterung „{0}“ des Inhaltsmappermoduls wird von mehreren Inhaltsmappern registriert.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "Die Dateierweiterung „{0}“ des Inhaltsmappers muss mit einem „.“ beginnen.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "Die Inhaltszuordnungs-Ausgabeerweiterung '{0}' darf nicht leer sein und muss mit \".\" beginnen.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Für die Zuordnung der Ausgabeerweiterung für die Inhaltszuordnung von '{0}' zu '{1}' muss \"rewriteRelativeImportExtensions\" aktiviert sein.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Die Ausgabeerweiterungszuordnungsquelle „{0}“ des Inhaltszuordnungsmoduls ist von dieser Inhaltszuordnung nicht registriert.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Die zusätzliche Ausgabedatei „{0}“ des Inhaltsmappers verursacht einen Konflikt mit einer vorhandenen Datei.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Für Inhaltsmapper muss das Befehlszeilenflag „--runExternalCode“ aktiviert sein.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Steuern Sie, welche Methode zum Erkennen von JS-Dateien im Modulformat verwendet wird.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Namespaces sind in globalen Skriptdateien nicht zulässig, wenn \"{0}\" aktiviert ist. Wenn diese Datei kein globales Skript sein soll, legen Sie \"moduleDetection\" auf \"force\" fest, oder fügen Sie eine leere \"export {}\"-Anweisung hinzu.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "Weder Decorators noch Modifizierer können auf \"this\"-Parameter angewendet werden.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Geschachtelte CommonJS-Exportkonstrukte können nicht für die Deklarationsausgabe serialisiert werden.", + "Network_path_0_is_not_supported_5116": "Der Netzwerkpfad „{0}“ wird nicht unterstützt.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Kein Basiskonstruktor weist die angegebene Anzahl von Typargumenten auf.", "No_constituent_of_type_0_is_callable_2755": "Es ist kein Bestandteil vom Typ \"{0}\" aufrufbar.", "No_constituent_of_type_0_is_constructable_2759": "Es kann kein Bestandteil vom Typ \"{0}\" erstellt werden.", @@ -1630,6 +1635,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Der Inhaltsmapper hat nicht innerhalb von {0} Sekunden auf die „initialize“-Anforderung geantwortet.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Der Inhaltsmapper hat „configIdentity“ nicht zurückgegeben. Dies ist erforderlich, wenn die Inhaltszuordnung im package.json '„dynamicConfig“: true' aufweist.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Das Content Mapper-Paket '{0}' konnte nicht aufgelöst werden.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Das Content Mapper-Paket „{0}“ befindet sich außerhalb des geöffneten Arbeitsbereichs und wird nicht geladen.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Der Inhaltsmapperprozess konnte nicht gestartet oder initialisiert werden.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Der Inhaltsmapperprozess wurde beendet, bevor er auf die Anforderung „initialize“ (Exitcode {0}) geantwortet hat.", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Fehler beim Inhaltsmapperprozess während der Verarbeitung der Projektanforderung.", @@ -1736,6 +1742,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Die Typen von \"{0}\" sind zwischen diesen Typen nicht kompatibel.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Die von \"{0}\" zurückgegebenen Typen sind zwischen diesen Typen nicht kompatibel.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "„typescript.contentMapper.exec“ des Inhaltsmapperpakets „{0}“ muss ein nicht leeres Zeichenfolgenarray sein.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "\"typescript.contentMapper.outputExtensions\" des Inhaltszuordnungspakets enthält '{0}' eine ungültige Zuordnung von '{1}' zu '{2}'. Erweiterungen dürfen nicht leer sein und müssen mit \".\" beginnen.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "„typescript.contentMapper.outputExtensions“ des Inhaltszuordnungspakets „{0}“ muss ein Objekt mit Zeichenfolgenwerten sein.", "The_value_0_cannot_be_used_here_18050": "Der Wert \"{0}\" kann hier nicht verwendet werden.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "Die Variablendeklaration einer for...in-Anweisung darf keinen Initialisierer aufweisen.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "Die Variablendeklaration einer for...of-Anweisung darf keinen Initialisierer aufweisen.", diff --git a/tsc/internal/diagnostics/loc/de-DE.json.gz b/tsc/internal/diagnostics/loc/de-DE.json.gz index 3834be5a9a127..831adfbcaf96e 100644 Binary files a/tsc/internal/diagnostics/loc/de-DE.json.gz and b/tsc/internal/diagnostics/loc/de-DE.json.gz differ diff --git a/tsc/internal/diagnostics/loc/es-ES.generated.json b/tsc/internal/diagnostics/loc/es-ES.generated.json index fa863ef749d11..1ba44794a6593 100644 --- a/tsc/internal/diagnostics/loc/es-ES.generated.json +++ b/tsc/internal/diagnostics/loc/es-ES.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Los constructores de las clases derivadas deben contener una llamada a \"super\".", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "El archivo contenedor no se ha especificado y no se puede determinar el directorio raíz. Se omitirá la búsqueda en la carpeta 'node_modules'.", "Containing_function_is_not_an_arrow_function_95128": "La función contenedora no es una función de flecha", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "La '{0}' de extensión del asignador de contenido no debe contener separadores de ruta de acceso.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "La extensión de archivo del asignador de contenido \"{0}\" es una extensión integrada y no la puede registrar un asignador de contenido.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "Hay más de un asignador de contenido registrado por la extensión de archivo del asignador de contenido '{0}'.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "La extensión de archivo del asignador de contenido \"{0}\" debe comenzar por '.'.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "La extensión de salida del asignador de contenido \"{0}\" no puede estar vacía y debe comenzar con \".\".", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "La asignación de la extensión de salida del asignador de contenido de '{0}' a '{1}' requiere que se habilite \"rewriteRelativeImportExtensions\".", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Este asignador de contenido no registró el origen de asignación de extensión de salida del asignador de contenido '{0}'.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "El archivo de salida complementario del asignador de contenido '{0}' entra en conflicto con un archivo existente.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Los asignadores de contenido requieren que se habilite la marca de línea de comandos \"--runExternalCode\".", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Controlar qué método se usa para detectar archivos JS con formato de módulo.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "No se permiten espacios de nombres en archivos de script globales cuando “{0}” está habilitado. Si este archivo no está pensado para ser un script global, establezca “moduleDetection” en “force” o agregue una instrucción “export {}” vacía.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "No se pueden aplicar modificadores ni decoradores a los parámetros “this”.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "No se pueden serializar construcciones de exportación de CommonJS anidadas para la emisión de declaraciones.", + "Network_path_0_is_not_supported_5116": "No se admite la ruta de acceso de red \"{0}\".", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "No hay ningún constructor base con el número especificado de argumentos de tipo.", "No_constituent_of_type_0_is_callable_2755": "No se puede llamar a ningún constituyente del tipo \"{0}\".", "No_constituent_of_type_0_is_constructable_2759": "No se puede construir ningún constituyente del tipo \"{0}\".", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "El asignador de contenido no respondió a la solicitud de inicialización en cuestión de {0} segundos.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "El asignador de contenido no devolvió \"configIdentity\", que es necesario cuando el asignador de contenido tiene \"dynamicConfig\": true\" en su package.json.", "The_content_mapper_package_0_could_not_be_resolved_18075": "No se pudo resolver el '{0}' del paquete del asignador de contenido.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "El paquete del asignador de contenido '{0}' está fuera del área de trabajo abierta y no se cargará.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "No se pudo iniciar o inicializar el proceso del asignador de contenido.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "El proceso del asignador de contenido se cerró antes de responder a la solicitud \"initialize\" (código {0}de salida).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Error en el proceso del asignador de contenido al controlar la solicitud del proyecto.", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Los tipos de \"{0}\" son incompatibles entre estos tipos.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Los tipos que \"{0}\" devuelve son incompatibles entre estos tipos.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "El archivo \"typescript.contentMapper.exec\" del paquete del asignador de contenido \"{0}\" debe ser una matriz de cadenas que no esté vacía.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "El elemento \"typescript.contentMapper.outputExtensions\" del paquete del asignador de contenido '{0}' contiene una asignación no válida de '{1}' a '{2}'. Las extensiones no deben estar vacías y comenzar con '.'.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "El valor de \"typescript.contentMapper.outputExtensions\" del paquete del asignador de contenido \"{0}\" debe ser un objeto con valores de cadena.", "The_value_0_cannot_be_used_here_18050": "El valor \"{0}\" no se puede usar aquí.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "La declaración de variable de una instrucción \"for...in\" no puede tener un inicializador.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "La declaración de variable de una instrucción \"for...of\" no puede tener un inicializador.", diff --git a/tsc/internal/diagnostics/loc/es-ES.json.gz b/tsc/internal/diagnostics/loc/es-ES.json.gz index cde61718c84c1..c466dabc1aa8a 100644 Binary files a/tsc/internal/diagnostics/loc/es-ES.json.gz and b/tsc/internal/diagnostics/loc/es-ES.json.gz differ diff --git a/tsc/internal/diagnostics/loc/fr-FR.generated.json b/tsc/internal/diagnostics/loc/fr-FR.generated.json index 2fa2f9852ede3..3d563f686eb15 100644 --- a/tsc/internal/diagnostics/loc/fr-FR.generated.json +++ b/tsc/internal/diagnostics/loc/fr-FR.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Les constructeurs pour les classes dérivées doivent contenir un appel de 'super'.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Fichier conteneur non spécifié et répertoire racine impossible à déterminer. Recherche ignorée dans le dossier 'node_modules'.", "Containing_function_is_not_an_arrow_function_95128": "La fonction conteneur n'est pas une fonction arrow", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "L’extension du mappeur de contenu « {0} » ne doit pas contenir de séparateurs de chemin d’accès.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "L’extension de fichier du mappeur de contenu '{0}' est une extension intégrée et ne peut pas être inscrite par un mappeur de contenu.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "L’extension de fichier du mappeur de contenu '{0}' est inscrite par plusieurs mappeurs de contenu.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "L’extension de fichier du mappeur de contenu « {0} » doit commencer par « . ».", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "L’extension de sortie du mappeur de contenu « {0} » ne doit pas être vide et doit commencer par un « . ».", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Le mappage de l’extension de sortie du mappeur de contenu de « {0} » à « {1} » nécessite l’activation de « rewriteRelativeImportExtensions ».", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "La source de mappage de l’extension de sortie du mappeur de contenu '{0}' n’est pas inscrite par ce mappeur de contenu.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Le fichier de sortie supplémentaire du mappeur de contenu '{0}' en conflit avec un fichier existant.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Les mappeurs de contenu nécessitent l’activation de l’indicateur de ligne de commande « --runExternalCode ».", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Contrôlez la méthode utilisée pour détecter les fichiers JS au format module.", @@ -1121,6 +1125,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Les espaces de noms ne sont pas autorisés dans les fichiers de script globaux lorsque «{0}» est activé. Si ce fichier n’est pas destiné à être un script global, définissez 'moduleDetection' sur 'force' ou ajoutez une instruction 'export {}' vide.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "Ni les décorateurs ni les modificateurs ne peuvent être appliqués aux paramètres « this ».", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Nous ne pouvons pas sérialiser des constructions d’exportation CommonJS imbriquées lors de l’émission de déclarations.", + "Network_path_0_is_not_supported_5116": "Le chemin d’accès « {0} » n’est pas pris en charge.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Aucun constructeur de base n'a le nombre spécifié d'arguments de type.", "No_constituent_of_type_0_is_callable_2755": "Aucun constituant de type '{0}' ne peut être appelé.", "No_constituent_of_type_0_is_constructable_2759": "Aucun constituant de type '{0}' ne peut être construit.", @@ -1630,6 +1635,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Le mappeur de contenu n’a pas répondu à la demande d’initialisation dans les {0} secondes.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Le mappeur de contenu n’a pas renvoyé 'configIdentity' qui est requis lorsque son fichier package.json contient '\"dynamicConfig\": true'.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Impossible de résoudre le '{0}' du package de mappage de contenu.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Le package de mappage de contenu '{0}' se trouve en dehors de l’espace de travail ouvert et ne sera pas chargé.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Nous n’avons pas pu démarrer ou initialiser le processus du mappeur de contenu.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Le processus de mappage de contenu s’est arrêté avant de répondre à la demande d’initialisation (code de sortie {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Nous n’avons pas pu effectuer le processus du mappeur de contenu lors du traitement de la requête de projet.", @@ -1736,6 +1742,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Les types de '{0}' sont incompatibles entre eux.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Les types retournés par '{0}' sont incompatibles entre eux.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "La propriété 'typescript.contentMapper.exec' du package du mappeur de contenu « {0} » doit être un tableau de chaînes non vide.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "Le paramètre 'typescript.contentMapper.outputExtensions' du package de mappage de contenu '{0}' contient un mappage non valide de '{1}' à '{2}'. Les extensions ne doivent pas être vides et doivent commencer par un '.'.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "Le 'typescript.contentMapper.outputExtensions' du package de mappeur de contenu '{0}' doit être un objet avec des valeurs de chaîne.", "The_value_0_cannot_be_used_here_18050": "La valeur '{0}' ne peut pas être utilisée ici.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "La déclaration de variable d'une instruction 'for...in' ne peut pas avoir d'initialiseur.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "La déclaration de variable d'une instruction 'for...of' ne peut pas avoir d'initialiseur.", diff --git a/tsc/internal/diagnostics/loc/fr-FR.json.gz b/tsc/internal/diagnostics/loc/fr-FR.json.gz index 8726081d6bf0f..e0c403ae09e9e 100644 Binary files a/tsc/internal/diagnostics/loc/fr-FR.json.gz and b/tsc/internal/diagnostics/loc/fr-FR.json.gz differ diff --git a/tsc/internal/diagnostics/loc/it-IT.generated.json b/tsc/internal/diagnostics/loc/it-IT.generated.json index 77e4ee8aeae91..797bc187f49b5 100644 --- a/tsc/internal/diagnostics/loc/it-IT.generated.json +++ b/tsc/internal/diagnostics/loc/it-IT.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "I costruttori di classi derivate devono contenere una chiamata 'super'.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Il file contenitore non è specificato e non è possibile determinare la directory radice. La ricerca nella cartella 'node_modules' verrà ignorata.", "Containing_function_is_not_an_arrow_function_95128": "La funzione contenitore non è una funzione arrow", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "L'estensione del mapper di contenuti '{0}' non deve contenere separatori di percorso.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "L'estensione di file del mapper di contenuti '{0}' è un'estensione predefinita e non può essere registrata da un mapper di contenuti.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "L'estensione di file del mapper di contenuti '{0}' è registrata da più di un mapper di contenuti.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "L'estensione del file di mapping del contenuto '{0}' deve iniziare con '.'.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "L'estensione di output del mapper di contenuto '{0}' non deve essere vuota e iniziare con un punto '.'.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Il mapping dell'estensione di output del mapper dei contenuti da '{0}' a '{1}' richiede l'abilitazione di 'rewriteRelativeImportExtensions'.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Il '{0}' di origine del mapping dell'estensione di output del mapper di contenuti non è registrato da questo mapper di contenuti.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Il file di output supplementare del mapper dei contenuti '{0}' è in conflitto con un file esistente.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "I mapper del contenuto richiedono l’abilitazione del flag della riga di comando '--runExternalCode'.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Controllare il metodo usato per rilevare i file JS in formato modulo.", @@ -1120,6 +1124,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Gli spazi dei nomi non sono consentiti nei file di script globali quando '{0}' è abilitato. Se questo file non deve essere uno script globale, impostare 'moduleDetection' su 'force' o aggiungere un'istruzione 'export {}' vuota.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "Non è possibile applicare né elementi Decorator né modificatori ai parametri 'this'.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Non è possibile serializzare costrutti di esportazione CommonJS annidati per la generazione delle dichiarazioni.", + "Network_path_0_is_not_supported_5116": "Il percorso della rete '{0}' non è supportato'.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Nessun costruttore di base contiene il numero specificato di argomenti tipo.", "No_constituent_of_type_0_is_callable_2755": "Non è possibile chiamare nessun costituente di tipo '{0}'.", "No_constituent_of_type_0_is_constructable_2759": "Non è possibile costruire nessun costituente di tipo '{0}'.", @@ -1629,6 +1634,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Il mapper di contenuto non ha risposto alla richiesta di 'initialize' entro {0} secondi.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Il mapper di contenuto non ha restituito 'configIdentity', che è obbligatorio quando nel relativo package.json è presente '\"dynamicConfig\": true'.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Non è stato possibile risolvere il pacchetto del mapper di contenuti '{0}'.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Il pacchetto del mapper di contenuto '{0}' si trova all'esterno dell'area di lavoro aperta e non verrà caricato.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Impossibile avviare o inizializzare il processo di mapping dei contenuti.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Il processo del mapper di contenuto è terminato prima di rispondere alla richiesta di 'initialize' (codice di uscita {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Errore del processo di mapping dei contenuti durante la gestione della richiesta di progetto.", @@ -1735,6 +1741,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "I tipi di '{0}' sono incompatibili tra questi tipi.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "I tipi restituiti da '{0}' sono incompatibili tra questi tipi.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "Il valore 'typescript.contentMapper.exec' del pacchetto del mapper di contenuti '{0}' deve essere una matrice non vuota di stringhe.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "'typescript.contentMapper.outputExtensions' del pacchetto del mapper di contenuti '{0}' contiene un mapping non valido da '{1}' a '{2}'. Le estensioni non devono essere vuote e iniziare con un punto '.'.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "Il valore 'typescript.contentMapper.outputExtensions' del pacchetto del mapper di contenuti '{0}' deve essere un oggetto con valori stringa.", "The_value_0_cannot_be_used_here_18050": "Non è possibile usare qui il valore '{0}'.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "La dichiarazione di variabile di un'istruzione 'for...in' non può contenere un inizializzatore.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "La dichiarazione di variabile di un'istruzione 'for...of' non può contenere un inizializzatore.", diff --git a/tsc/internal/diagnostics/loc/it-IT.json.gz b/tsc/internal/diagnostics/loc/it-IT.json.gz index 72c2fcbf45775..647d8315e098c 100644 Binary files a/tsc/internal/diagnostics/loc/it-IT.json.gz and b/tsc/internal/diagnostics/loc/it-IT.json.gz differ diff --git a/tsc/internal/diagnostics/loc/ja-JP.generated.json b/tsc/internal/diagnostics/loc/ja-JP.generated.json index b61cfbbb91e40..0e471727cfdb8 100644 --- a/tsc/internal/diagnostics/loc/ja-JP.generated.json +++ b/tsc/internal/diagnostics/loc/ja-JP.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "派生クラスのコンストラクターには 'super' の呼び出しを含める必要があります。", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "包含するファイルが指定されていないため、ルート ディレクトリを決定できません。'node_modules' フォルダーのルックアップをスキップします。", "Containing_function_is_not_an_arrow_function_95128": "含まれている関数はアロー関数ではありません", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "コンテンツ マッパー拡張子 '{0}' にパス区切り記号を含めることはできません。", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "コンテンツ マッパーのファイル拡張子 '{0}' は組み込みの拡張子であるため、コンテンツ マッパーでは登録できません。", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "コンテンツ マッパーのファイル拡張子 '{0}' は、複数のコンテンツ マッパーによって登録されています。", "Content_mapper_file_extension_0_must_begin_with_a_18065": "コンテンツ マッパーのファイル拡張子 '{0}' は、'.' で始まる必要があります。", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "コンテンツ マッパーの出力拡張子 '{0}' は空ではなく、'.' で始まる必要があります。", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "コンテンツ マッパーの出力拡張子を '{0}' から '{1}' にマッピングするには、'rewriteRelativeImportExtensions' を有効にする必要があります。", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "コンテンツ マッパー出力拡張子マッピングのソース '{0}' は、このコンテンツ マッパーに登録されていません。", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "コンテンツ マッパーの補足出力ファイル '{0}' が既存のファイルと競合しています。", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "コンテンツ マッパーでは、'--runExternalCode' コマンド ライン フラグを有効にする必要があります。", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "モジュール形式の JS ファイルを検出するために使用するメソッドを制御します。", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "'{0}' が有効になっている場合、グローバル スクリプト ファイルでは名前空間を使用できません。このファイルがグローバル スクリプトを意図していない場合は、'moduleDetection' を 'force' に設定するか、空の 'export {}' ステートメントを追加してください。", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "デコレーターも修飾子も 'this' パラメーターに適用できません。", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "入れ子になった CommonJS エクスポート構造は、宣言出力のためにシリアル化できません。", + "Network_path_0_is_not_supported_5116": "ネットワーク パス '{0}' はサポートされていません。", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "指定した数の型引数を持つ基底コンストラクターは存在しません。", "No_constituent_of_type_0_is_callable_2755": "型 '{0}' の構成要素は呼び出し可能ではありません。", "No_constituent_of_type_0_is_constructable_2759": "型 '{0}' の構成要素はコンストラクト可能ではありません。", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "コンテンツ マッパーが {0} 秒以内に 'initialize' 要求に応答しませんでした。", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "コンテンツ マッパーが 'configIdentity' を返しませんでした。これは、コンテンツ マッパーの package.json に '\"dynamicConfig\": true' がある場合は必須です。", "The_content_mapper_package_0_could_not_be_resolved_18075": "コンテンツ マッパー パッケージ '{0}' を解決できませんでした。", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "コンテンツ マッパー パッケージ '{0}' は、開いているワークスペースの外部にあるため、読み込まれません。", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "コンテンツ マッパー プロセスを開始または初期化できませんでした。", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "コンテンツ マッパー プロセスが、'initialize' 要求に応答する前に終了しました (終了コード {0})。", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "コンテンツ マッパー プロセスが、プロジェクト要求の処理中に失敗しました。", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "'{0}' の型は、これらの型同士で互換性がありません。", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "'{0}' によって返された型は、これらの型同士で互換性がありません。", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "コンテンツ マッパー パッケージ '{0}' の 'typescript.contentMapper.exec' は、空でない文字列の配列である必要があります。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "コンテンツ マッパー パッケージ '{0}' の 'typescript.contentMapper.outputExtensions' に、'{1}' から '{2}' への無効なマッピングが含まれています。拡張子は空にできず、'.' で始まる必要があります。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "コンテンツ マッパー パッケージ '{0}' の 'typescript.contentMapper.outputExtensions' は、文字列値を持つオブジェクトである必要があります。", "The_value_0_cannot_be_used_here_18050": "値 '{0}' はここでは使用できません。", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "'for...in' ステートメントの変数宣言に初期化子を指定することはできません。", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "'for...of' ステートメントの変数宣言に初期化子を指定することはできません。", diff --git a/tsc/internal/diagnostics/loc/ja-JP.json.gz b/tsc/internal/diagnostics/loc/ja-JP.json.gz index afafe4189d32a..3409e0d806684 100644 Binary files a/tsc/internal/diagnostics/loc/ja-JP.json.gz and b/tsc/internal/diagnostics/loc/ja-JP.json.gz differ diff --git a/tsc/internal/diagnostics/loc/ko-KR.generated.json b/tsc/internal/diagnostics/loc/ko-KR.generated.json index dc8a3a60624b1..6bff29942fce3 100644 --- a/tsc/internal/diagnostics/loc/ko-KR.generated.json +++ b/tsc/internal/diagnostics/loc/ko-KR.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "파생 클래스의 생성자는 'super' 호출을 포함해야 합니다.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "포함 파일이 지정되지 않았고 루트 디렉터리를 확인할 수 없어 'node_modules' 폴더 조회를 건너뜁니다.", "Containing_function_is_not_an_arrow_function_95128": "포함 함수가 화살표 함수가 아닙니다.", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "콘텐츠 매퍼 확장 '{0}'에는 경로 구분 기호가 포함되어서는 안됩니다.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "콘텐츠 매퍼 파일 확장명 '{0}'은(는) 기본 제공 확장명이므로 콘텐츠 매퍼에 등록할 수 없습니다.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "둘 이상의 콘텐츠 매퍼에서 '{0}' 콘텐츠 매퍼 파일 확장명을 등록했습니다.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "콘텐츠 매퍼 파일 확장명 '{0}'는 '.'로 시작해야 합니다.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "콘텐츠 매퍼 출력 확장 '{0}'은(는) 비어 있지 않아야 하며 '.'로 시작해야 합니다.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "'{0}'에서 '{1}'(으)로 콘텐츠 매퍼 출력 확장 매핑을 사용하려면 'rewriteRelativeImportExtensions'를 사용하도록 설정해야 합니다.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "콘텐츠 매퍼 출력 확장 매핑 원본 '{0}'이(가) 이 콘텐츠 매퍼에 등록되지 않았습니다.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "콘텐츠 매퍼의 보조 출력 파일 '{0}'이(가) 기존 파일과 충돌합니다.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "콘텐츠 매퍼를 사용하려면 '--runExternalCode' 명령줄 플래그를 사용하도록 설정해야 합니다.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "모듈 형식의 JS 파일을 감지하는 데 사용되는 방법을 제어합니다.", @@ -1121,6 +1125,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "'{0}'이(가) 사용하도록 설정된 경우 전역 스크립트 파일에서는 네임스페이스를 사용할 수 없습니다. 이 파일이 전역 스크립트가 아닌 경우 'moduleDetection'을 'force'로 설정하거나 빈 'export {}' 문을 추가합니다.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "데코레이터와 한정자를 'this' 매개 변수에 적용할 수 없습니다.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "선언을 방출할 때 중첩된 CommonJS 내보내기 구문을 직렬화할 수 없습니다.", + "Network_path_0_is_not_supported_5116": "네트워크 경로 '{0}'은(는) 지원되지 않습니다.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "기본 생성자에 지정된 수의 형식 인수가 없습니다.", "No_constituent_of_type_0_is_callable_2755": "'{0}' 형식의 구성원을 호출할 수 없습니다.", "No_constituent_of_type_0_is_constructable_2759": "'{0}' 형식의 구성원을 생성할 수 없습니다.", @@ -1630,6 +1635,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "콘텐츠 매퍼가 {0}초 이내에 'initialize' 요청에 응답하지 않았습니다.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "콘텐츠 매퍼가 'configIdentity'를 반환하지 않았습니다. 콘텐츠 매퍼의 package.json에 '\"dynamicConfig\": true'가 선언된 경우 필수입니다.", "The_content_mapper_package_0_could_not_be_resolved_18075": "콘텐츠 매퍼 패키지 '{0}' 확인할 수 없습니다.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "콘텐츠 매퍼 패키지 '{0}'이(가) 열린 작업 영역 외부에 있으므로 로드되지 않습니다.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "콘텐츠 매퍼 프로세스를 시작하거나 초기화하지 못했습니다.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "콘텐츠 매퍼 프로세스가 'initialize' 요청에 응답하기 전에 종료되었습니다(종료 코드 {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "콘텐츠 매퍼 프로세스가 프로젝트 요청을 처리하는 동안 오류가 발생했습니다.", @@ -1736,6 +1742,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "'{0}'의 형식은 해당 형식 간에 호환되지 않습니다.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "'{0}'에서 반환되는 형식은 해당 형식 간에 호환되지 않습니다.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "콘텐츠 매퍼 패키지 '{0}'의 'typescript.contentMapper.exec'는 비어 있지 않은 문자열 배열이어야 합니다.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "콘텐츠 매퍼 패키지의 'typescript.contentMapper.outputExtensions' '{0}'에 '{1}'에서 '{2}'로의 잘못된 매핑이 포함되어 있습니다. 확장명은 비워 둘 수 없으며 '.'로 시작해야 합니다.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "콘텐츠 매퍼 패키지 '{0}'의 'typescript.contentMapper.outputExtensions'는 문자열 값이 있는 개체여야 합니다.", "The_value_0_cannot_be_used_here_18050": "여기서는 '{0}' 값을 사용할 수 없습니다.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "'for...in' 문의 변수 선언에 이니셜라이저가 포함될 수 없습니다.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "'for...of' 문의 변수 선언에 이니셜라이저가 포함될 수 없습니다.", diff --git a/tsc/internal/diagnostics/loc/ko-KR.json.gz b/tsc/internal/diagnostics/loc/ko-KR.json.gz index 21bb28dc6f382..9004bcd6f9790 100644 Binary files a/tsc/internal/diagnostics/loc/ko-KR.json.gz and b/tsc/internal/diagnostics/loc/ko-KR.json.gz differ diff --git a/tsc/internal/diagnostics/loc/pl-PL.generated.json b/tsc/internal/diagnostics/loc/pl-PL.generated.json index 4931e43329c00..280dae3539a12 100644 --- a/tsc/internal/diagnostics/loc/pl-PL.generated.json +++ b/tsc/internal/diagnostics/loc/pl-PL.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Konstruktory klas pochodnych muszą zawierać wywołanie „super”.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Nie podano pliku zawierającego i nie można określić katalogu głównego. Pomijanie wyszukiwania w folderze „node_modules”.", "Containing_function_is_not_an_arrow_function_95128": "Funkcja zawierająca nie jest funkcją strzałki", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "Rozszerzenie mapera zawartości „{0}” nie może zawierać separatorów ścieżek.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "Rozszerzenie pliku mapera zawartości '{0}' jest rozszerzeniem wbudowanym i nie może zostać zarejestrowane przez mapera zawartości.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "Rozszerzenie pliku mapera zawartości '{0}' jest zarejestrowane przez więcej niż jeden maper zawartości.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "Rozszerzenie pliku mapera zawartości „{0}” musi zaczynać się znakiem „.”.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "Rozszerzenie danych wyjściowych mapera zawartości „{0}” musi być niepuste i rozpoczynać się od znaku \".\".", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Mapowanie rozszerzenia danych wyjściowych mapera zawartości od „{0}” do „{1}” wymaga włączenia elementu „rewriteRelativeImportExtensions”.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Źródłowy '{0}' mapowania rozszerzenia danych wyjściowych mapera zawartości nie został zarejestrowany przez ten program mapowania zawartości.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Dodatkowy plik danych wyjściowych mapera zawartości „{0}” powoduje konflikt z istniejącym plikiem.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Mapery zawartości wymagają włączenia flagi wiersza polecenia „--runExternalCode”.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Określ, jaka metoda jest używana do wykrywania plików JS w formacie modułu.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Przestrzenie nazw są niedozwolone w globalnych plikach skryptów, gdy włączona jest opcja „{0}”. Jeśli ten plik nie jest przeznaczony do bycia skryptem globalnym, ustaw parametr „moduleDetection” na wartość „force” lub dodaj pustą instrukcję „export {}”.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "Do parametrów „this” nie można stosować dekoratorów ani modyfikatorów.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Nie można zserializować zagnieżdżonych konstrukcji eksportu CommonJS dla emisji deklaracji.", + "Network_path_0_is_not_supported_5116": "Ścieżka sieciowa „{0}” nie jest obsługiwana.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Żaden z konstruktorów podstawowych nie ma określonej liczby argumentów typu.", "No_constituent_of_type_0_is_callable_2755": "Żadna składowa typu „{0}” nie jest wywoływalna.", "No_constituent_of_type_0_is_constructable_2759": "Żadnej składowej typu „{0}” nie można skonstruować.", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Maper zawartości nie odpowiedział na żądanie „initialize” w ciągu {0} sekund.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Maper zawartości nie zwrócił elementu „configIdentity”, co jest wymagane, gdy maper zawartości ma element „dynamicConfig\": true” w pliku package.json.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Nie można rozpoznać '{0}' pakietu mapowania zawartości.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Pakiet mapowania zawartości '{0}' znajduje się poza otwartym obszarem roboczym i nie zostanie załadowany.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Nie można uruchomić lub zainicjować procesu mapowania zawartości.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Proces mapera zawartości zakończył się przed odpowiedzią na żądanie „initialize” (kod zakończenia {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Proces mapera zawartości nie powiódł się podczas obsługi żądania projektu.", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Typy elementu „{0}” są niezgodne między tymi typami.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Typy zwrócone przez element „{0}” są niezgodne między tymi typami.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "Element „typescript.contentMapper.exec” pakietu mapera zawartości „{0}” musi być niepustą tablicą ciągów.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "Element „typescript.contentMapper.outputExtensions” pakietu mapera zawartości „{0}” zawiera nieprawidłowe mapowanie od „{1}” do „{2}”. Rozszerzenia muszą być niepuste i zaczynać się od znaku \".\".", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "Element „typescript.contentMapper.outputExtensions” pakietu mapera zawartości „{0}” musi być obiektem z wartościami ciągów.", "The_value_0_cannot_be_used_here_18050": "W tym miejscu nie można użyć wartości „{0}”.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "Deklaracja zmiennej instrukcji „for...in” nie może mieć inicjatora.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "Deklaracja zmiennej instrukcji „for...of” nie może mieć inicjatora.", diff --git a/tsc/internal/diagnostics/loc/pl-PL.json.gz b/tsc/internal/diagnostics/loc/pl-PL.json.gz index 63d425689962d..10e5dfccc4fa5 100644 Binary files a/tsc/internal/diagnostics/loc/pl-PL.json.gz and b/tsc/internal/diagnostics/loc/pl-PL.json.gz differ diff --git a/tsc/internal/diagnostics/loc/pt-BR.generated.json b/tsc/internal/diagnostics/loc/pt-BR.generated.json index a3e83850ca5ca..22004724ae391 100644 --- a/tsc/internal/diagnostics/loc/pt-BR.generated.json +++ b/tsc/internal/diagnostics/loc/pt-BR.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Construtores para classes derivadas devem conter uma chamada 'super'.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "O arquivo contido não foi especificado e o diretório raiz não pode ser determinado, ignorando a pesquisa na pasta 'node_modules'.", "Containing_function_is_not_an_arrow_function_95128": "A função contentora não é uma função de seta", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "A extensão '{0}' do mapeador de conteúdo não deve conter separadores de caminho.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "A extensão de arquivo do mapeador de conteúdo '{0}' é uma extensão interna e não pode ser registrada por um mapeador de conteúdo.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "A extensão de arquivo do mapeador de conteúdo \"{0}\" está registrada por mais de um mapeador de conteúdo.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "A extensão de arquivo do mapeador de conteúdo \"{0}\" deve começar com \".\".", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "A extensão de saída do mapeador de conteúdo '{0}' deve ser não vazia e começar com um '.'.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "O mapeamento da extensão de saída do mapeador de conteúdo de '{0}' para '{1}' exige que 'rewriteRelativeImportExtensions' esteja habilitado.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "A origem do mapeamento da extensão de saída do mapeador de conteúdo '{0}' não está registrada por este mapeador de conteúdo.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "O arquivo de saída suplementar do mapeador de conteúdo '{0}' entra em conflito com um arquivo existente.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Os mapeadores de conteúdo exigem que a opção de linha de comando '--runExternalCode' esteja habilitada.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Controlar qual método é usado para detectar arquivos JS no formato de módulo.", @@ -1121,6 +1125,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Namespaces não são permitidos em arquivos de script global quando '{0}' está habilitado. Se esse arquivo não se destina a ser um script global, defina 'moduleDetection' como 'force' ou adicione uma instrução 'export {}' vazia.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "Nem decoradores nem modificadores podem ser aplicados a parâmetros 'this'.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Construções de exportação de CommonJS aninhadas não podem ser serializadas para a emissão de declarações.", + "Network_path_0_is_not_supported_5116": "O caminho de rede '{0}' não tem suporte.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Nenhum construtor base tem o número especificado de argumentos de tipo.", "No_constituent_of_type_0_is_callable_2755": "Nenhum membro do tipo '{0}' pode ser chamado.", "No_constituent_of_type_0_is_constructable_2759": "Nenhum membro do tipo '{0}' é construído.", @@ -1630,6 +1635,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "O mapeador de conteúdo não respondeu à solicitação 'initialize' em {0} segundos.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "O mapeador de conteúdo não retornou 'configIdentity', o que é necessário quando o mapeador de conteúdo tem '\"dynamicConfig\": true' em seu package.json.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Não foi possível resolver o pacote do mapeador de conteúdo '{0}'.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "O pacote do mapeador de conteúdo '{0}' está fora do workspace aberto e não será carregado.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Não foi possível iniciar ou inicializar o processo do mapeador de conteúdo.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "O processo do mapeador de conteúdo foi encerrado antes de responder à solicitação 'initialize' (código de saída {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "O processo do mapeador de conteúdo falhou ao processar a solicitação do projeto.", @@ -1736,6 +1742,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Os tipos de '{0}' são incompatíveis entre esses tipos.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Os tipos retornados por '{0}' são incompatíveis entre esses tipos.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "A propriedade 'typescript.contentMapper.exec' do pacote do mapeador de conteúdo '{0}' deve ser uma matriz não vazia de cadeias de caracteres.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "O 'typescript.contentMapper.outputExtensions' do pacote do mapeador de conteúdo '{0}' contém um mapeamento inválido de '{1}' para '{2}'. As extensões não podem estar vazias e devem começar com '.'.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "O 'typescript.contentMapper.outputExtensions' do pacote do mapeador de conteúdo '{0}' deve ser um objeto com valores de cadeia de caracteres.", "The_value_0_cannot_be_used_here_18050": "O valor '{0}' não pode ser usado aqui.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "A declaração de variável de uma instrução 'for...in' não pode ter um inicializador.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "A declaração de variável de uma instrução 'for...of' não pode ter um inicializador.", diff --git a/tsc/internal/diagnostics/loc/pt-BR.json.gz b/tsc/internal/diagnostics/loc/pt-BR.json.gz index 076d901435f49..d58af68b93213 100644 Binary files a/tsc/internal/diagnostics/loc/pt-BR.json.gz and b/tsc/internal/diagnostics/loc/pt-BR.json.gz differ diff --git a/tsc/internal/diagnostics/loc/ru-RU.generated.json b/tsc/internal/diagnostics/loc/ru-RU.generated.json index 5d5f61279150c..a2915acd79d97 100644 --- a/tsc/internal/diagnostics/loc/ru-RU.generated.json +++ b/tsc/internal/diagnostics/loc/ru-RU.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Конструкторы производных классов должны содержать вызов super.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Содержащий файл не указан, корневой каталог невозможно определить. Выполняется пропуск поиска в папке node_modules.", "Containing_function_is_not_an_arrow_function_95128": "Содержащая функция не является стрелочной", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "Расширение сопоставителя содержимого {0} не должно содержать разделители путей.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "Расширение файла преобразователя содержимого \"{0}\" является встроенным расширением и не может быть зарегистрировано преобразователем содержимого.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "Расширение файла преобразователя содержимого \"{0}\" зарегистрировано более чем одним преобразователем содержимого.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "Расширение файла преобразователя содержимого \"{0}\" должно начинаться с \".\".", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "Выходной '{0}' расширения сопоставителя содержимого не должен быть пустым и начинаться с \".\".", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "Для сопоставления расширения выходных данных сопоставителя содержимого из '{0}' в '{1}' необходимо включить параметр \"rewriteRelativeImportExtensions\".", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "Источник сопоставления расширения выходных данных сопоставителя содержимого {0} не зарегистрирован этим сопоставителем содержимого.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "Дополнительный выходной файл сопоставителя содержимого '{0}' конфликтует с существующим файлом.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "Для преобразователей содержимого необходимо включить флаг командной строки '--runExternalCode'.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Выбирайте метод, который нужно использовать для обнаружения JS-файлов в формате модуля.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "Пространства имен не допускаются в глобальных файлах сценариев, если включено \"{0}\". Если этот файл не предназначен для использования в качестве глобального сценария, установите для параметра \"moduleDetection\" значение \"force\" или добавьте пустой оператор \"export {}\".", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "К параметрам \"this\" нельзя применять декораторы и модификаторы.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "Вложенные конструкции экспорта CommonJS нельзя сериализовать для выпуска объявления.", + "Network_path_0_is_not_supported_5116": "Сетевой путь \"{0}\" не поддерживается.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Ни один конструктор базового класса не имеет указанного числа аргументов типа.", "No_constituent_of_type_0_is_callable_2755": "Нет составляющей типа \"{0}\", которую можно вызвать.", "No_constituent_of_type_0_is_constructable_2759": "Нет составляющей типа \"{0}\", которую можно создать.", @@ -1378,7 +1383,7 @@ "Referenced_project_0_must_have_setting_composite_Colon_true_6306": "Указанный в ссылке проект \"{0}\" должен иметь следующее значение параметра composite: true.", "Referenced_via_0_from_file_1_1400": "Ссылка с помощью \"{0}\" из файла \"{1}\"", "Regular_expression_pattern_modifiers_are_only_available_when_targeting_0_or_later_18062": "Модификаторы шаблонов регулярных выражений доступны только при выборе в качестве цели \"{0}\" или более поздней версии.", - "Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_n_2834": "Относительные пути импорта требуют явных расширений файлов при импорте ECMAScript, если '--moduleResolution' имеет значение 'node16' или 'nodenext'. Рассмотрите возможность добавления расширения к пути импорта.", + "Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_n_2834": "Относительные пути импорта требуют явных расширений файлов при импорте ECMAScript, если \"--moduleResolution\" имеет значение \"node16\" или \"nodenext\". Рассмотрите возможность добавления расширения к пути импорта.", "Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_n_2835": "Относительные пути импорта требуют явных расширений файлов при импорте ECMAScript, если \"--moduleResolution\" имеет значение \"node16\" или \"nodenext\". Вы хотели использовать \"{0}\"?", "Remove_Unused_Imports_95207": "Удалить неиспользованные импортированные данные", "Remove_a_list_of_directories_from_the_watch_process_6628": "Удалить список каталогов из процесса просмотра.", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "Сопоставитель содержимого не ответил на запрос \"initialize\" в течение {0} сек.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "Сопоставитель содержимого не вернул \"configIdentity\", что требуется, если в package.json сопоставителя содержимого объявлено \"dynamicConfig\": true.", "The_content_mapper_package_0_could_not_be_resolved_18075": "Не удалось разрешить пакет сопоставителя содержимого \"{0}\".", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "Пакет сопоставителя содержимого \"{0}\" находится вне открытой рабочей области и не будет загружен.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "Не удалось запустить или инициализировать процесс сопоставителя содержимого.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "Процесс сопоставителя содержимого завершился до получения ответа на запрос \"initialize\" (код выхода {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "Сбой процесса сопоставителя содержимого при обработке запроса проекта.", @@ -1737,11 +1743,13 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "Типы \"{0}\" несовместимы между этими типами.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "Типы, возвращаемые \"{0}\", несовместимы между этими типами.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "Значение \"typescript.contentMapper.exec\" пакета сопоставителя содержимого \"{0}\" должно быть непустым массивом строк.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "\"typescript.contentMapper.outputExtensions\" пакета сопоставителя содержимого '{0}' содержит недопустимое сопоставление между '{1}' и '{2}'. Расширения не должны быть пустыми и начинаться с \".\".", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "Значение свойства 'typescript.contentMapper.outputExtensions' пакета сопоставления содержимого {0} должно быть объектом со строковыми значениями.", "The_value_0_cannot_be_used_here_18050": "Здесь нельзя использовать значение \"{0}\".", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "Объявление переменной оператора for...in не может содержать инициализатор.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "Объявление переменной оператора for...of не может содержать инициализатор.", "The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any_2410": "Оператор with не поддерживается. Все символы в блоке with получат тип any.", - "There_are_types_at_0_but_this_result_could_not_be_resolved_under_your_current_moduleResolution_setti_6280": "Существуют типы в ' {0} ', но этот результат не удалось разрешить с помощью текущей настройки 'moduleResolution'. Рассмотрите возможность обновления до \"node16\", \"nodenext\" или \"bundler\".", + "There_are_types_at_0_but_this_result_could_not_be_resolved_under_your_current_moduleResolution_setti_6280": "Существуют типы в \"{0}\", но этот результат не удалось разрешить с помощью текущего параметра \"moduleResolution\". Рассмотрите возможность обновления до \"node16\", \"nodenext\" или \"bundler\".", "There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The__6278": "Существуют типы в ' {0} ', но этот результат не удалось разрешить при соблюдении \"экспорта\" package.json. ' {1} ' может потребоваться обновить свой package.json или типизацию.", "There_is_no_capturing_group_named_0_in_this_regular_expression_1532": "В этом регулярном выражении нет группы захвата с именем \" {0} \".", "There_is_nothing_available_for_repetition_1507": "Нет ничего, что можно было бы повторить.", @@ -1869,7 +1877,7 @@ "Type_containing_private_name_0_can_t_be_used_with_isolatedDeclarations_9039": "Тип, содержащий частное имя \" {0} \", нельзя использовать с --isolatedDeclarations.", "Type_declaration_files_to_be_included_in_compilation_6124": "Файлы объявления типа, включаемые в компиляцию.", "Type_expected_1110": "Ожидался тип.", - "Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require_1456": "Утверждения импорта типа должны иметь ровно один ключ \"resolution-mode\" со значением \"import\" или \"require\".", + "Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require_1456": "Утверждения импорта типа должны иметь ровно один ключ `resolution-mode` со значением `import` или `require`.", "Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute_1542": "Импорт типа модуля ECMAScript из модуля CommonJS должен иметь атрибут \"resolution-mode\".", "Type_instantiation_is_excessively_deep_and_possibly_infinite_2589": "Создание экземпляра типа является слишком глубоким и, возможно, бесконечным.", "Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method_1062": "На тип есть прямые или непрямые ссылки в обратном вызове выполнения собственного метода then.", @@ -2184,9 +2192,9 @@ "readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature_1024": "Модификатор readonly может отображаться только в объявлении свойства или сигнатуре индекса.", "readonly_type_modifier_is_only_permitted_on_array_and_tuple_literal_types_1354": "Модификатор типа \"readonly\" допускается только для типов литерала массива и кортежа.", "require_call_may_be_converted_to_an_import_80005": "Вызов \"require\" можно преобразовать в \"import\".", - "resolution_mode_can_only_be_set_for_type_only_imports_1454": "\"resolution-mode\" можно задать лишь для импорта, распространяющегося только на тип.", - "resolution_mode_is_the_only_valid_key_for_type_import_assertions_1455": "\"resolution-mode\" является единственным допустимым ключом для утверждений импорта типа.", - "resolution_mode_should_be_either_require_or_import_1453": "\"resolution-mode\" должен иметь значение \"require\" или \"import\".", + "resolution_mode_can_only_be_set_for_type_only_imports_1454": "`resolution-mode` можно задать лишь для импорта, распространяющегося только на тип.", + "resolution_mode_is_the_only_valid_key_for_type_import_assertions_1455": "`resolution-mode` является единственным допустимым ключом для утверждений импорта типа.", + "resolution_mode_should_be_either_require_or_import_1453": "`resolution-mode` должен иметь значение `require` или `import`.", "rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0_6107": "Параметр \"rootDirs\" задан; он используется для разрешения относительного имени модуля \"{0}\".", "super_can_only_be_referenced_in_a_derived_class_2335": "Ссылка на super может указываться только в производном классе.", "super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions_2660": "На super можно ссылаться только в элементах производных классов или литеральных выражениях объекта.", @@ -2206,7 +2214,7 @@ "true_for_ES2022_and_above_including_ESNext_6930": "true для ES2022 и выше, включая ESNext.", "true_if_composite_false_otherwise_6909": "\"true\", если \"composite\", \"false\" в противном случае", "true_unless_strict_is_false_6905": "`true`, если `strict` имеет значение `false`", - "true_when_moduleResolution_is_node16_nodenext_or_bundler_otherwise_false_6411": "`true`, когда moduleResolution — это node16, nodenext или Bundler; в противном случае `false`.", + "true_when_moduleResolution_is_node16_nodenext_or_bundler_otherwise_false_6411": "`true`, когда \"moduleResolution\" — это \"node16\", \"nodenext\" или \"bundler\"; в противном случае используется значение `false`.", "tsc_Colon_The_TypeScript_Compiler_6922": "TSC: компилятор TypeScript", "tsconfig_json_is_present_but_will_not_be_loaded_if_files_are_specified_on_commandline_Use_ignoreConf_5112": "Файл tsconfig.json присутствует, но не будет загружен, если в командной строке указаны файлы. Используйте \"--ignoreConfig\", чтобы пропустить эту ошибку.", "type_Colon_6902": "тип:", diff --git a/tsc/internal/diagnostics/loc/ru-RU.json.gz b/tsc/internal/diagnostics/loc/ru-RU.json.gz index 1edb434eccdf1..169fdda17d96f 100644 Binary files a/tsc/internal/diagnostics/loc/ru-RU.json.gz and b/tsc/internal/diagnostics/loc/ru-RU.json.gz differ diff --git a/tsc/internal/diagnostics/loc/tr-TR.generated.json b/tsc/internal/diagnostics/loc/tr-TR.generated.json index a576ce3832755..8e1f96324b08b 100644 --- a/tsc/internal/diagnostics/loc/tr-TR.generated.json +++ b/tsc/internal/diagnostics/loc/tr-TR.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "Türetilmiş sınıflara ilişkin oluşturucular bir 'super' çağrısı içermelidir.", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "Kapsayıcı dosya belirtilmedi ve kök dizini belirlenemiyor; 'node_modules' klasöründe arama atlanıyor.", "Containing_function_is_not_an_arrow_function_95128": "İçeren işlev bir ok işlevi değil", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "İçerik eşleyici uzantısı '{0}' yol ayırıcıları içermemelidir.", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "İçerik eşleyici dosya '{0}' uzantısı, yerleşik bir uzantıdır ve içerik eşleyici tarafından kaydedilemez.", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "İçerik eşleyicisi dosya uzantısı '{0}' birden fazla içerik eşleyici tarafından kaydedildi.", "Content_mapper_file_extension_0_must_begin_with_a_18065": "İçerik eşleyici dosya '{0}' uzantısı '.' ile başlamalıdır.", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "İçerik eşleyici çıkış uzantısı '{0}' boş olamaz ve '.' ile başlamalıdır.", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "'{0}' ile '{1}' arasında içerik eşleyici çıkış uzantısı eşlemesi için 'rewriteRelativeImportExtensions' etkinleştirilmelidir.", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "İçerik eşleyici çıkış uzantısı eşleme kaynağı '{0}', bu içerik eşleyici tarafından kaydedilmemiş.", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "İçerik eşleyici ek çıkış dosyası '{0}' var olan bir dosyayla çakışıyor.", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "İçerik eşleyicileri için '--runExternalCode' komut satırı bayrağının etkinleştirilmesi gerekir.", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "Modül biçimli JS dosyalarını algılamak için kullanılan yöntemi denetle.", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "'{0}' etkinken genel betik dosyalarında ad alanlarına izin verilmiyor. Bu dosyanın genel bir betik olması amaçlanmamışsa, 'moduleDetection'i 'force' olarak ayarlayın veya boş bir 'export {}' deyimi ekleyin.", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "'this' parametrelerine ne dekoratörler ne de değiştiriciler uygulanamaz.", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "İç içe CommonJS dışarı aktarma yapıları bildirim yayma için serileştirilemez.", + "Network_path_0_is_not_supported_5116": "'{0}' ağ yolu desteklenmiyor.", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "Hiçbir temel oluşturucu, belirtilen tür bağımsız değişkeni sayısına sahip değil.", "No_constituent_of_type_0_is_callable_2755": "'{0}' türünde çağrılabilir bileşen yok.", "No_constituent_of_type_0_is_constructable_2759": "'{0}' türünde oluşturulabilir bileşen yok.", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "İçerik eşleyicisi, 'initialize' isteğine {0} saniye içinde yanıt vermedi.", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "İçerik eşleyicisi, package.json dosyasında '\"dynamicConfig\": true' bulunduğunda gerekli olan 'configIdentity' değerini döndürmedi.", "The_content_mapper_package_0_could_not_be_resolved_18075": "İçerik eşleyicisi paketi '{0}' çözümlenemedi.", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "'{0}' içerik eşleyici paketi açık çalışma alanının dışında olduğundan yüklenmeyecek.", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "İçerik eşleyicisi işlemi başlatılamadı veya başlatma işlemi tamamlanamadı.", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "İçerik eşleyicisi işlemi, 'initialize' isteğine yanıt vermeden önce sonlandı (çıkış kodu {0}).", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "İçerik eşleyicisi işlemi, proje isteği işlenirken başarısız oldu.", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "'{0}' türleri bu türler arasında uyumsuz.", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "'{0}' tarafından döndürülen türler, bu türler arasında uyumsuz.", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "İçerik eşleyicisi paketi '{0}' içindeki 'typescript.contentMapper.exec' özelliği, boş olmayan bir dize dizisi olmalıdır.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "İçerik eşleyici paketinin 'typescript.contentMapper.outputExtensions' değeri'{0}' '{1}' ile '{2}' arasında geçersiz bir eşleme içeriyor. Uzantılar boş olmamalı ve '.' ile başlamalıdır.", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "'typescript.contentMapper.outputExtensions' özelliği, '{0}' içerik eşleyici paketinde dize değerlerine sahip bir nesne olmalıdır.", "The_value_0_cannot_be_used_here_18050": "'{0}' değeri burada kullanılamaz.", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "'for...in' deyiminin değişken bildirimi bir başlatıcıya sahip olamaz.", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "'for...of' deyiminin değişken bildirimi bir başlatıcıya sahip olamaz.", diff --git a/tsc/internal/diagnostics/loc/tr-TR.json.gz b/tsc/internal/diagnostics/loc/tr-TR.json.gz index b0d2b71161f13..e9e5b28bc345a 100644 Binary files a/tsc/internal/diagnostics/loc/tr-TR.json.gz and b/tsc/internal/diagnostics/loc/tr-TR.json.gz differ diff --git a/tsc/internal/diagnostics/loc/zh-CN.generated.json b/tsc/internal/diagnostics/loc/zh-CN.generated.json index b74d89d0010a3..c536bd5a79f03 100644 --- a/tsc/internal/diagnostics/loc/zh-CN.generated.json +++ b/tsc/internal/diagnostics/loc/zh-CN.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "派生类的构造函数必须包含 \"super\" 调用。", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "未指定包含文件,并且无法确定根目录,正在跳过在 \"node_modules\" 文件夹中查找。", "Containing_function_is_not_an_arrow_function_95128": "包含函数不是箭头函数", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "内容映射器扩展 '{0}' 不得包含路径分隔符。", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "内容映射器文件扩展名 '{0}' 是内置扩展名,不能由内容映射器注册。", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "内容映射器文件扩展名“{0}”已由多个内容映射器注册。", "Content_mapper_file_extension_0_must_begin_with_a_18065": "内容映射器文件扩展名 '{0}' 必须以 “.” 开头。", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "内容映射器输出扩展插件“{0}”必须为非空,并以 '.' 开头。", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "从“{0}”到“{1}”的内容映射器输出扩展映射需要启用 'rewriteRelativeImportExtensions'。", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "此内容映射程序未注册内容映射程序输出扩展插件映射源 '{0}'。", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "内容映射器补充输出文件“{0}”与现有文件冲突。", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "内容映射器要求启用 \"--runExternalCode\" 命令行标志。", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "控制用于检测模块格式 JS 文件的方法。", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "启用“{0}”时,全局脚本文件中不允许使用命名空间。如果此文件不是全局脚本,请将“moduleDetection”设置为“force”或添加空的“export {}”语句。", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "修饰器和修饰符都不能应用于“this”参数。", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "无法为声明发出序列化嵌套 CommonJS 导出构造。", + "Network_path_0_is_not_supported_5116": "网络路径 '{0}' 不受支持。", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "没有任何基构造函数具有指定数量的类型参数。", "No_constituent_of_type_0_is_callable_2755": "不可调用 \"{0}\" 类型的任何组成部分。", "No_constituent_of_type_0_is_constructable_2759": "不可构造 \"{0}\" 类型的任何组成部分。", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "内容映射器未在 {0} 秒内响应“初始化”请求。", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "内容映射器未返回 \"configIdentity\";当内容映射器在其 package.json 中具有 '\"dynamicConfig\": true' 时,必须返回此属性。", "The_content_mapper_package_0_could_not_be_resolved_18075": "无法解析内容映射器包“{0}”。", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "内容映射器包 '{0}' 在打开的工作区之外,将不会加载。", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "无法启动或初始化内容映射器进程。", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "内容映射器进程在响应“初始化”请求之前退出(退出代码 {0})。", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "内容映射器进程在处理项目请求时失败。", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "在这些类型中,\"{0}\" 的类型不兼容。", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "在这些类型中,\"{0}\" 返回的类型不兼容。", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "内容映射器包“{0}”的 \"typescript.contentMapper.exec\" 必须是非空字符串数组。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "内容映射器包“{0}”的“ 'typescript.contentMapper.outputExtensions' 包含从“{1}”到“{2}”的无效映射。扩展必须为非空,并以 '.' 开头。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "内容映射器包“{0}”的 'typescript.contentMapper.outputExtensions' 必须是带有字符串值的对象。", "The_value_0_cannot_be_used_here_18050": "此处不能使用值“{0}”。", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "\"for...in\" 语句的变量声明不能有初始化表达式。", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "\"for...of\" 语句的变量声明不能有初始化表达式。", diff --git a/tsc/internal/diagnostics/loc/zh-CN.json.gz b/tsc/internal/diagnostics/loc/zh-CN.json.gz index 70860134fe3e9..94ca54a00ecd7 100644 Binary files a/tsc/internal/diagnostics/loc/zh-CN.json.gz and b/tsc/internal/diagnostics/loc/zh-CN.json.gz differ diff --git a/tsc/internal/diagnostics/loc/zh-TW.generated.json b/tsc/internal/diagnostics/loc/zh-TW.generated.json index c4f279802008c..fe8d6eadfaf44 100644 --- a/tsc/internal/diagnostics/loc/zh-TW.generated.json +++ b/tsc/internal/diagnostics/loc/zh-TW.generated.json @@ -495,9 +495,13 @@ "Constructors_for_derived_classes_must_contain_a_super_call_2377": "衍生類別的建構函式必須包含 'super' 呼叫。", "Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_mod_6126": "未指定包含檔案,因此無法決定根目錄,而將略過 'node_modules' 中的查閱。", "Containing_function_is_not_an_arrow_function_95128": "內含函式不是箭頭函式", + "Content_mapper_extension_0_must_not_contain_path_separators_18123": "內容映射器副檔名 '{0}' 不得包含路徑分隔符號。", "Content_mapper_file_extension_0_is_a_built_in_extension_and_cannot_be_registered_by_a_content_mapper_18066": "內容對應工具檔案副檔名 '{0}' 是內建副檔名,無法由內容對應工具註冊。", "Content_mapper_file_extension_0_is_registered_by_more_than_one_content_mapper_18067": "內容對應程式副檔名 '{0}' 已由多個內容對應程式登錄。", "Content_mapper_file_extension_0_must_begin_with_a_18065": "內容對應工具檔案副檔名 '{0}' 必須以 '.' 開頭。", + "Content_mapper_output_extension_0_must_be_non_empty_and_begin_with_a_18118": "內容映射器輸出副檔名 '{0}' 不得為空白,且必須以 '.' 開頭。", + "Content_mapper_output_extension_mapping_from_0_to_1_requires_rewriteRelativeImportExtensions_to_be_e_18120": "內容映射器輸出副檔名從 '{0}' 對應至 '{1}' 時,必須啟用 'rewriteRelativeImportExtensions'。", + "Content_mapper_output_extension_mapping_source_0_is_not_registered_by_this_content_mapper_18119": "內容映射器輸出副檔名對應的來源 '{0}' 未向此內容映射器註冊。", "Content_mapper_supplemental_output_file_0_conflicts_with_an_existing_file_18096": "內容對應工具補充輸出檔案 '{0}' 與現有檔案衝突。", "Content_mappers_require_the_runExternalCode_command_line_flag_to_be_enabled_18068": "內容對應工具需要啟用 '--runExternalCode' 命令列旗標。", "Control_what_method_is_used_to_detect_module_format_JS_files_1475": "控制用來偵測模組格式 JS 檔案的方法。", @@ -1122,6 +1126,7 @@ "Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to__1280": "啟用 '{0}' 時,在全域指令碼檔案中不允許命名空間。如果此檔案不是預定為全域指令碼,將 'moduleDetection' 設定為 'force',或新增空白的 'export {}' 陳述式。", "Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters_1433": "裝飾項目和修飾元都無法套用至 'this' 參數。", "Nested_CommonJS_export_constructs_cannot_be_serialized_for_declaration_emit_6425": "宣告產生作業無法序列化巢狀的 CommonJS 匯出結構。", + "Network_path_0_is_not_supported_5116": "不支援網路路徑 '{0}'。", "No_base_constructor_has_the_specified_number_of_type_arguments_2508": "沒有任何基底建構函式具有指定的類型引數數量。", "No_constituent_of_type_0_is_callable_2755": "無法呼叫 '{0}' 類型的任何構件。", "No_constituent_of_type_0_is_constructable_2759": "無法建構 '{0}' 類型的任何構件。", @@ -1631,6 +1636,7 @@ "The_content_mapper_did_not_respond_to_the_initialize_request_within_0_seconds_18102": "內容對應工具未在 {0} 秒內回應 'initialize' 要求。", "The_content_mapper_did_not_return_configIdentity_which_is_required_when_the_content_mapper_has_dynam_18092": "內容對應工具未傳回 'configIdentity',而當內容對應工具在其 package.json 中具有 '\"dynamicConfig\": true' 時,這是必要的。", "The_content_mapper_package_0_could_not_be_resolved_18075": "無法解析內容對應工具套件 '{0}'。", + "The_content_mapper_package_0_is_outside_the_open_workspace_and_will_not_be_loaded_18124": "內容對應器套件 '{0}' 位於開啟的工作區之外,因此不會載入。", "The_content_mapper_process_could_not_be_started_or_initialized_18084": "無法啟動或初始化內容對應工具程序。", "The_content_mapper_process_exited_before_responding_to_the_initialize_request_exit_code_0_18101": "內容對應工具程序在回應 'initialize' 要求之前就結束了 (結束代碼 {0})。", "The_content_mapper_process_failed_while_handling_the_project_request_18091": "內容對應工具程序在處理專案要求時失敗。", @@ -1737,6 +1743,8 @@ "The_types_of_0_are_incompatible_between_these_types_2200": "'{0}' 的類型在這些類型之間不相容。", "The_types_returned_by_0_are_incompatible_between_these_types_2201": "'{0}' 所傳回的類型在這些類型之間不相容。", "The_typescript_contentMapper_exec_of_the_content_mapper_package_0_must_be_a_non_empty_array_of_strin_18079": "內容對應工具套件 '{0}' 中的 'typescript.contentMapper.exec' 必須是非空字串陣列。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_contains_an_invalid_ma_18121": "內容映射器套件 '{0}' 的 'typescript.contentMapper.outputExtensions' 包含從 '{1}' 對應至 '{2}' 的無效對應。副檔名不得為空白,且必須以 '.' 開頭。", + "The_typescript_contentMapper_outputExtensions_of_the_content_mapper_package_0_must_be_an_object_with_18122": "內容映射器套件 '{0}' 的 'typescript.contentMapper.outputExtensions' 必須是字串值的物件。", "The_value_0_cannot_be_used_here_18050": "此處無法使用值 '{0}'。", "The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer_1189": "'for...in' 陳述式的變數宣告不得有初始設定式。", "The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer_1190": "'for...of' 陳述式的變數宣告不得有初始設定式。", diff --git a/tsc/internal/diagnostics/loc/zh-TW.json.gz b/tsc/internal/diagnostics/loc/zh-TW.json.gz index 123618277d263..46af228785dac 100644 Binary files a/tsc/internal/diagnostics/loc/zh-TW.json.gz and b/tsc/internal/diagnostics/loc/zh-TW.json.gz differ diff --git a/tsc/internal/execute/build/orchestrator.go b/tsc/internal/execute/build/orchestrator.go index 664e47844616e..23d96c74fae22 100644 --- a/tsc/internal/execute/build/orchestrator.go +++ b/tsc/internal/execute/build/orchestrator.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "runtime" "slices" "strings" "sync/atomic" @@ -889,18 +890,27 @@ func (o *Orchestrator) buildOrCleanOrder(order []*BuildTask) *OrchestratorResult scheduleOrder = o.computeScheduleOrder(order) } // Builders pick up selected projects in dependency-depth order; reporting preserves the selected build order. - reported := make(chan struct{}) - go func() { - defer close(reported) + if o.opts.Command.CompilerOptions.SingleThreaded.IsTrue() || runtime.GOARCH == "wasm" { + o.rangeTasks(scheduleOrder, func(path tspath.PathKey, task *BuildTask) { + o.buildOrCleanProject(task, path) + }) for _, task := range order { - <-task.built task.report(o, task.path, buildResult) } - }() - o.rangeTasks(scheduleOrder, func(path tspath.PathKey, task *BuildTask) { - o.buildOrCleanProject(task, path) - }) - <-reported + } else { + reported := make(chan struct{}) + go func() { + defer close(reported) + for _, task := range order { + <-task.built + task.report(o, task.path, buildResult) + } + }() + o.rangeTasks(scheduleOrder, func(path tspath.PathKey, task *BuildTask) { + o.buildOrCleanProject(task, path) + }) + <-reported + } } else { // Circularity errors prevent any project from being built buildResult.Result.Status = tsc.ExitStatusProjectReferenceCycle_OutputsSkipped @@ -920,7 +930,7 @@ func (o *Orchestrator) rangeTask(f func(path tspath.PathKey, task *BuildTask)) { func (o *Orchestrator) rangeTasks(order []*BuildTask, f func(path tspath.PathKey, task *BuildTask)) { numRoutines := 4 - if o.opts.Command.CompilerOptions.SingleThreaded.IsTrue() { + if o.opts.Command.CompilerOptions.SingleThreaded.IsTrue() || runtime.GOARCH == "wasm" { numRoutines = 1 } else if builders := o.opts.Command.BuildOptions.Builders; builders != nil { numRoutines = *builders diff --git a/tsc/internal/execute/incremental/affectedfileshandler.go b/tsc/internal/execute/incremental/affectedfileshandler.go index 50ca58e8f676b..b3828155ed197 100644 --- a/tsc/internal/execute/incremental/affectedfileshandler.go +++ b/tsc/internal/execute/incremental/affectedfileshandler.go @@ -230,7 +230,7 @@ func (h *affectedFilesHandler) handleDtsMayChangeOfAffectedFile(dtsMayChange dts var done func() // If exported const enum, we need to ensure that js files are emitted as well since the const enum value changed if affectedFile.Symbol != nil { - for _, exported := range affectedFile.Symbol.Exports() { + for _, exported := range affectedFile.Symbol.Exports().All() { if exported.Flags()&ast.SymbolFlagsConstEnum != 0 { invalidateJsFiles = true break diff --git a/tsc/internal/execute/tsc.go b/tsc/internal/execute/tsc.go index c41e0bb25c921..fa69f3b620425 100644 --- a/tsc/internal/execute/tsc.go +++ b/tsc/internal/execute/tsc.go @@ -317,6 +317,11 @@ func performIncrementalCompilation( tr := startTracingIfNeeded(sys, config, testing) + if testing == nil && config.CompilerOptions().MaxMemory == nil { + core.SuspendGC() + } + parser.EnableLazyDts(testing == nil && (config.CompilerOptions().SkipLibCheck.IsTrue() || config.CompilerOptions().NoCheck.IsTrue())) + defer parser.EnableLazyDts(false) parseStart := sys.Now() program := compiler.NewProgram(compiler.ProgramOptions{ Config: config, @@ -349,7 +354,8 @@ func performIncrementalCompilation( testing.OnProgram(incrementalProgram) } return tsc.CommandLineResult{ - Status: result.Status, + Status: result.Status, + Diagnostics: result.Diagnostics, } } @@ -378,12 +384,18 @@ func performCompilation( tr := startTracingIfNeeded(sys, config, testing) + if testing == nil && config.CompilerOptions().MaxMemory == nil { + core.SuspendGC() + } + parser.EnableLazyDts(testing == nil && (config.CompilerOptions().SkipLibCheck.IsTrue() || config.CompilerOptions().NoCheck.IsTrue())) + defer parser.EnableLazyDts(false) parseStart := sys.Now() program := compiler.NewProgram(compiler.ProgramOptions{ Config: config, Host: host, Tracing: tr, }) + compiler.FinishWasmProgramInit(program) compileTimes.ParseTime = sys.Now().Sub(parseStart) if contentMapperHost != nil { compileTimes.ContentMapperTimes = contentMapperHost.Timings() @@ -404,7 +416,8 @@ func performCompilation( stopTracing(sys, tr) return tsc.CommandLineResult{ - Status: result.Status, + Status: result.Status, + Diagnostics: result.Diagnostics, } } diff --git a/tsc/internal/execute/tsc/compile.go b/tsc/internal/execute/tsc/compile.go index 7ea291edc3f36..91057905334cf 100644 --- a/tsc/internal/execute/tsc/compile.go +++ b/tsc/internal/execute/tsc/compile.go @@ -63,10 +63,10 @@ type Watcher interface { } type CommandLineResult struct { - Status ExitStatus - Watcher Watcher + Status ExitStatus + Watcher Watcher + Diagnostics []*ast.Diagnostic } - type CommandLineTesting interface { // Ensure that all emitted files are timestamped in order to ensure they are deterministic for test baseline OnEmittedFiles(result *compiler.EmitResult, mTimesCache *collections.SyncMap[tspath.PathKey, time.Time]) diff --git a/tsc/internal/execute/tsc/emit.go b/tsc/internal/execute/tsc/emit.go index 4f30b1617ada1..7521d67055e4b 100644 --- a/tsc/internal/execute/tsc/emit.go +++ b/tsc/internal/execute/tsc/emit.go @@ -54,11 +54,7 @@ func EmitAndReportStatistics(input EmitInput) (CompileAndEmitResult, *Statistics if input.Config.CompilerOptions().Diagnostics.IsTrue() || input.Config.CompilerOptions().ExtendedDiagnostics.IsTrue() { var memStats runtime.MemStats - // GC must be called twice to allow things to settle. - runtime.GC() - runtime.GC() runtime.ReadMemStats(&memStats) - statistics = statisticsFromProgram(input, &memStats) statistics.Report(input.Writer, input.Testing) } diff --git a/tsc/internal/execute/tsc/statistics.go b/tsc/internal/execute/tsc/statistics.go index ce2ac6d5d4bdc..55f0b0dd27d98 100644 --- a/tsc/internal/execute/tsc/statistics.go +++ b/tsc/internal/execute/tsc/statistics.go @@ -64,6 +64,7 @@ type Statistics struct { symbols int types int instantiations int + retiredCheckers int memoryUsed uint64 memoryAllocs uint64 compileTimes *CompileTimes @@ -71,15 +72,16 @@ type Statistics struct { func statisticsFromProgram(input EmitInput, memStats *runtime.MemStats) *Statistics { return &Statistics{ - files: len(input.Program.SourceFiles()), - lines: input.Program.LineCount(), - identifiers: input.Program.IdentifierCount(), - symbols: input.Program.SymbolCount(), - types: input.Program.TypeCount(), - instantiations: input.Program.InstantiationCount(), - memoryUsed: memStats.Alloc, - memoryAllocs: memStats.Mallocs, - compileTimes: input.CompileTimes, + files: len(input.Program.SourceFiles()), + lines: input.Program.LineCount(), + identifiers: input.Program.IdentifierCount(), + symbols: input.Program.SymbolCount(), + types: input.Program.TypeCount(), + instantiations: input.Program.InstantiationCount(), + retiredCheckers: input.Program.RetiredCheckerCount(), + memoryUsed: memStats.Alloc, + memoryAllocs: memStats.Mallocs, + compileTimes: input.CompileTimes, } } @@ -103,6 +105,9 @@ func (s *Statistics) Report(w io.Writer, testing CommandLineTesting) { table.add(prefix+"Symbols", s.symbols) table.add(prefix+"Types", s.types) table.add(prefix+"Instantiations", s.instantiations) + if s.retiredCheckers != 0 { + table.add(prefix+"Checkers retired", s.retiredCheckers) + } table.add(prefix+"Memory used", fmt.Sprintf("%vK", s.memoryUsed/1024)) table.add(prefix+"Memory allocs", strconv.FormatUint(s.memoryAllocs, 10)) if s.compileTimes.ConfigTime != 0 { diff --git a/tsc/internal/execute/tsctests/tsc_test.go b/tsc/internal/execute/tsctests/tsc_test.go index 91fefc49365b5..ee429a826f94a 100644 --- a/tsc/internal/execute/tsctests/tsc_test.go +++ b/tsc/internal/execute/tsctests/tsc_test.go @@ -5305,6 +5305,90 @@ func TestGenerateTrace(t *testing.T) { }, commandLineArgs: []string{"--generateTrace", "/home/src/workspaces/project/trace", "--singleThreaded"}, }, + { + // Type ids are creation order. Each part of this project makes the checker create types while it + // iterates a symbol table with more than eight entries (whose iteration order varies from run to run): + // the infer type parameters of a conditional type in a declaration file, a namespace import whose + // members are aliases of namespace imports, and declaration emit of a type no import of main.ts + // exports, which resolves the `export =` re-exports of every module. + subScenario: "generateTrace creates types in a stable order", + files: generateTraceStableOrderFiles(), + commandLineArgs: []string{"--generateTrace", "/home/src/workspaces/project/trace", "--singleThreaded"}, + }, + { + subScenario: "generateTrace creates index signature sibling types in a stable order", + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "ambient.d.ts"] + }`), + "/home/src/workspaces/project/ambient.d.ts": stringtestutil.Dedent(` + declare const k1: string; + declare class C { + [k1]: { a: 1 }; + m1: { p1: 1 }; + m2: { p2: 2 }; + m3: { p3: 3 }; + m4: { p4: 4 }; + m5: { p5: 5 }; + m6: { p6: 6 }; + m7: { p7: 7 }; + m8: { p8: 8 }; + m9: { p9: 9 }; + m10: { p10: 10 }; + } + `), + "/home/src/workspaces/project/main.ts": stringtestutil.Dedent(` + declare function expand(): { [K in keyof X]: X[K] }; + export const w = expand(); + `), + }, + commandLineArgs: []string{"--generateTrace", "/home/src/workspaces/project/trace", "--singleThreaded"}, + }, + { + subScenario: "generateTrace creates static index signature sibling types in a stable order", + files: FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "ambient.d.ts"] + }`), + "/home/src/workspaces/project/ambient.d.ts": stringtestutil.Dedent(` + declare const k1: string; + declare class C { + static [k1]: { a: 1 }; + static m1: { p1: 1 }; + static m2: { p2: 2 }; + static m3: { p3: 3 }; + static m4: { p4: 4 }; + static m5: { p5: 5 }; + static m6: { p6: 6 }; + static m7: { p7: 7 }; + static m8: { p8: 8 }; + static m9: { p9: 9 }; + static m10: { p10: 10 }; + } + `), + "/home/src/workspaces/project/main.ts": stringtestutil.Dedent(` + declare function expand(): { [K in keyof X]: X[K] }; + export const w = expand(); + `), + }, + commandLineArgs: []string{"--generateTrace", "/home/src/workspaces/project/trace", "--singleThreaded"}, + }, } for _, c := range cases { @@ -5312,6 +5396,57 @@ func TestGenerateTrace(t *testing.T) { } } +func generateTraceStableOrderFiles() FileMap { + files := FileMap{ + "/home/src/workspaces/project/tsconfig.json": stringtestutil.Dedent(` + { + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "locales/index.d.ts"] + }`), + "/home/src/workspaces/project/fields.d.ts": stringtestutil.Dedent(` + export type Fields = T extends { a: infer A; b: infer B; c: infer C; d: infer D; e: infer E; f: infer F; g: infer G; h: infer H; i: infer I; j: infer J; k: infer K; l: infer L } ? [A, B, C, D, E, F, G, H, I, J, K, L] : never; + `), + "/home/src/workspaces/project/gadget.d.ts": stringtestutil.Dedent(` + export interface Gadget { + id: number; + } + `), + "/home/src/workspaces/project/widget.d.ts": stringtestutil.Dedent(` + import { Gadget } from "./gadget"; + export declare function makeWidget(): Gadget; + `), + "/home/src/workspaces/project/main.ts": stringtestutil.Dedent(` + import * as parts from "./parts/index"; + import { Fields } from "./fields"; + import { makeWidget } from "./widget"; + + export const fields: Fields<{ a: 1; b: 2; c: 3; d: 4; e: 5; f: 6; g: 7; h: 8; i: 9; j: 10; k: 11; l: 12 }> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + export const allParts = parts; + export const widget = makeWidget(); + `), + } + var localesIndex, partsIndex strings.Builder + parts := []string{"alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu"} + for _, locale := range []string{"ar", "de", "en", "es", "fr", "he", "it", "ja", "ko", "nl", "pl", "pt"} { + files["/home/src/workspaces/project/locales/"+locale+".d.ts"] = fmt.Sprintf("declare const _default: () => { locale: %q };\nexport = _default;\n", locale) + fmt.Fprintf(&localesIndex, "export { default as %s } from \"./%s\";\n", locale, locale) + } + for _, part := range parts { + files["/home/src/workspaces/project/parts/"+part+".d.ts"] = fmt.Sprintf("export declare const %s: { name: %q };\n", part, part) + fmt.Fprintf(&partsIndex, "import * as %s from \"./%s\";\n", part, part) + } + fmt.Fprintf(&partsIndex, "export { %s };\n", strings.Join(parts, ", ")) + files["/home/src/workspaces/project/locales/index.d.ts"] = localesIndex.String() + files["/home/src/workspaces/project/parts/index.d.ts"] = partsIndex.String() + return files +} + func TestTscContentMapperEmit(t *testing.T) { t.Parallel() (&tscInput{ diff --git a/tsc/internal/format/scanner.go b/tsc/internal/format/scanner.go index d1ac0882ea150..4eeb2e5770ac6 100644 --- a/tsc/internal/format/scanner.go +++ b/tsc/internal/format/scanner.go @@ -152,6 +152,10 @@ func (s *formattingScanner) shouldRescanJsxText(node *ast.Node) bool { return s.lastTokenInfo.token.Kind == ast.KindJsxText } +func shouldRescanLessThanSlashToken(container *ast.Node) bool { + return container.Kind == ast.KindJsxClosingElement || container.Kind == ast.KindJsxClosingFragment +} + func shouldRescanSlashToken(container *ast.Node) bool { return container.Kind == ast.KindRegularExpressionLiteral } @@ -179,6 +183,7 @@ const ( actionRescanJsxIdentifier actionRescanJsxText actionRescanJsxAttributeValue + actionRescanLessThanSlashToken ) func fixTokenKind(tokenInfo tokenInfo, container *ast.Node) tokenInfo { @@ -207,6 +212,8 @@ func (s *formattingScanner) readTokenInfo(n *ast.Node) tokenInfo { expectedScanAction = actionRescanJsxText } else if shouldRescanJsxAttributeValue(n) { expectedScanAction = actionRescanJsxAttributeValue + } else if shouldRescanLessThanSlashToken(n) { + expectedScanAction = actionRescanLessThanSlashToken } else { expectedScanAction = actionScan } @@ -301,6 +308,12 @@ func (s *formattingScanner) getNextToken(n *ast.Node, expectedScanAction scanAct case actionRescanJsxAttributeValue: s.lastScanAction = actionRescanJsxAttributeValue return s.s.ReScanJsxAttributeValue() + case actionRescanLessThanSlashToken: + if token == ast.KindLessThanToken { + // The parser scans `{x}; +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.FormatDocument(t, "") + f.VerifyCurrentFileContent(t, "{x};\n") +} diff --git a/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsteriskAfterText_test.go b/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsteriskAfterText_test.go new file mode 100644 index 0000000000000..2a2f71543f212 --- /dev/null +++ b/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsteriskAfterText_test.go @@ -0,0 +1,20 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestFormatJsxClosingTagFollowedByAsteriskAfterText(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.tsx +text; +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.FormatDocument(t, "") + f.VerifyCurrentFileContent(t, "text;\n") +} diff --git a/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsterisk_test.go b/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsterisk_test.go new file mode 100644 index 0000000000000..64407f8155633 --- /dev/null +++ b/tsc/internal/fourslash/tests/formatJsxClosingTagFollowedByAsterisk_test.go @@ -0,0 +1,20 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestFormatJsxClosingTagFollowedByAsterisk(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.tsx +<>; +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.FormatDocument(t, "") + f.VerifyCurrentFileContent(t, "<>;\n") +} diff --git a/tsc/internal/fourslash/tests/formatNestedJSDocNullableType_test.go b/tsc/internal/fourslash/tests/formatNestedJSDocNullableType_test.go new file mode 100644 index 0000000000000..5627f30c5be72 --- /dev/null +++ b/tsc/internal/fourslash/tests/formatNestedJSDocNullableType_test.go @@ -0,0 +1,20 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestFormatNestedJSDocNullableType(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.ts +let x: ??string; +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.FormatDocument(t, "") + f.VerifyCurrentFileContent(t, "let x: ??string;\n") +} diff --git a/tsc/internal/fourslash/tests/formatTypeArgumentStartingWithLessThan_test.go b/tsc/internal/fourslash/tests/formatTypeArgumentStartingWithLessThan_test.go new file mode 100644 index 0000000000000..47a5a635abfe9 --- /dev/null +++ b/tsc/internal/fourslash/tests/formatTypeArgumentStartingWithLessThan_test.go @@ -0,0 +1,20 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestFormatTypeArgumentStartingWithLessThan(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `// @Filename: /a.ts +f<(x: T) => T>(g); +` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.FormatDocument(t, "") + f.VerifyCurrentFileContent(t, "f<(x: T) => T>(g);\n") +} diff --git a/tsc/internal/fswatch/debounce.go b/tsc/internal/fswatch/debounce.go index 71428f2571a3c..8e6dc98eaf912 100644 --- a/tsc/internal/fswatch/debounce.go +++ b/tsc/internal/fswatch/debounce.go @@ -1,6 +1,7 @@ package fswatch import ( + "runtime" "sync" "time" ) @@ -42,7 +43,9 @@ func newDebounce() *debounce { d := &debounce{ callbacks: make(map[any]func()), } - go d.loop() + if runtime.GOARCH != "wasm" { + go d.loop() + } return d } diff --git a/tsc/internal/ls/autoimport/extract.go b/tsc/internal/ls/autoimport/extract.go index 12ea044255167..8a6f98ef6fcd3 100644 --- a/tsc/internal/ls/autoimport/extract.go +++ b/tsc/internal/ls/autoimport/extract.go @@ -111,7 +111,7 @@ func (e *exportExtractor) extractFromFile(file *ast.SourceFile) []*Export { var exportCount int for _, statement := range file.Statements.Nodes { if ast.IsModuleWithStringLiteralName(statement) && isNonPatternAmbientModuleDeclaration(file, statement.AsModuleDeclaration()) { - exportCount += len(statement.AsModuleDeclaration().Symbol.Exports()) + exportCount += statement.AsModuleDeclaration().Symbol.Exports().Len() } } exports := make([]*Export, 0, exportCount) @@ -144,11 +144,11 @@ func (e *exportExtractor) extractFromModule(file *ast.SourceFile) []*Export { }) var augmentationExportCount int for _, decl := range moduleAugmentations { - augmentationExportCount += len(decl.Symbol.Exports()) + augmentationExportCount += decl.Symbol.Exports().Len() } moduleID := e.getModuleID(file) - exports := make([]*Export, 0, len(file.Symbol.Exports())+augmentationExportCount) - for name, symbol := range file.Symbol.Exports() { + exports := make([]*Export, 0, file.Symbol.Exports().Len()+augmentationExportCount) + for name, symbol := range file.Symbol.Exports().All() { e.extractFromSymbol(name, symbol, moduleID, file.FileName(), file, &exports) } for _, decl := range moduleAugmentations { @@ -176,7 +176,7 @@ func (e *exportExtractor) extractFromModule(file *ast.SourceFile) []*Export { } func (e *exportExtractor) extractFromModuleDeclaration(decl *ast.ModuleDeclaration, file *ast.SourceFile, moduleID ModuleID, moduleFileName tspath.RootedFilePath, exports *[]*Export) { - for name, symbol := range decl.Symbol.Exports() { + for name, symbol := range decl.Symbol.Exports().All() { e.extractFromSymbol(name, symbol, moduleID, moduleFileName, file, exports) } } @@ -191,7 +191,7 @@ func (e *symbolExtractor) extractFromSymbol(name string, symbol *ast.Symbol, mod allExports := e.checker.GetExportsOfModule(symbol.Parent()) // allExports includes named exports from the file that will be processed separately; // we want to add only the ones that come from the star - for name, namedExport := range symbol.Parent().Exports() { + for name, namedExport := range symbol.Parent().Exports().All() { if name != ast.InternalSymbolNameExportStar { idx := slices.Index(allExports, namedExport) if idx >= 0 || shouldIgnoreSymbol(namedExport) { @@ -231,8 +231,8 @@ func (e *symbolExtractor) extractFromSymbol(name string, symbol *ast.Symbol, mod if target != nil { if syntax == ExportSyntaxEquals && target.Flags()&ast.SymbolFlagsNamespace != 0 { - *exports = slices.Grow(*exports, len(target.Exports())) - for innerName, namedExport := range target.Exports() { + *exports = slices.Grow(*exports, target.Exports().Len()) + for innerName, namedExport := range target.Exports().All() { if innerName != ast.InternalSymbolNameExportStar { export, _ := e.createExport(namedExport, moduleID, moduleFileName, syntax, file, checkerLease) if export != nil { @@ -251,7 +251,7 @@ func (e *symbolExtractor) extractFromSymbol(name string, symbol *ast.Symbol, mod *exports = slices.Grow(*exports, len(expression.AsObjectLiteralExpression().Properties.Nodes)) for _, prop := range expression.AsObjectLiteralExpression().Properties.Nodes { if ast.IsShorthandPropertyAssignment(prop) || ast.IsPropertyAssignment(prop) && prop.AsPropertyAssignment().Name().Kind == ast.KindIdentifier { - export, _ := e.createExport(expression.Symbol().Members()[prop.Name().Text()], moduleID, moduleFileName, syntax, file, checkerLease) + export, _ := e.createExport(expression.Symbol().Members().Get(prop.Name().Text()), moduleID, moduleFileName, syntax, file, checkerLease) if export != nil { export.through = name *exports = append(*exports, export) diff --git a/tsc/internal/ls/autoimport/registry.go b/tsc/internal/ls/autoimport/registry.go index 4b8a71042a41a..ec92e50b18066 100644 --- a/tsc/internal/ls/autoimport/registry.go +++ b/tsc/internal/ls/autoimport/registry.go @@ -752,7 +752,7 @@ func (b *registryBuilder) markBucketsDirty(change RegistryChange, logger *loggin // Look up the package name for granular updates packageName := entry.Value().Paths[path] entry.Change(func(bucket *RegistryBucket) { bucket.markNodeModulesDirty(packageName) }) - if !entry.Value().state.multipleFilesDirty { + if entry.Value().state.multipleFilesDirty { delete(cleanNodeModulesBuckets, dirPath) } } @@ -764,7 +764,7 @@ func (b *registryBuilder) markBucketsDirty(change RegistryChange, logger *loggin if packageName, ok := entry.Value().Paths[path]; ok { // Use the package name for granular updates entry.Change(func(bucket *RegistryBucket) { bucket.markNodeModulesDirty(packageName) }) - if !entry.Value().state.multipleFilesDirty { + if entry.Value().state.multipleFilesDirty { delete(cleanNodeModulesBuckets, bucketDirPath) } } @@ -780,7 +780,7 @@ func (b *registryBuilder) markBucketsDirty(change RegistryChange, logger *loggin if _, ok := entry.Value().Paths[path]; ok { // Project buckets don't use package-based granular updates entry.Change(func(bucket *RegistryBucket) { bucket.markProjectFileDirty(path) }) - if !entry.Value().state.multipleFilesDirty { + if entry.Value().state.multipleFilesDirty { delete(cleanProjectBuckets, projectDirPath) } } diff --git a/tsc/internal/ls/autoimport/registry_test.go b/tsc/internal/ls/autoimport/registry_test.go index 996c73cd0ceea..41628c23aac87 100644 --- a/tsc/internal/ls/autoimport/registry_test.go +++ b/tsc/internal/ls/autoimport/registry_test.go @@ -1276,3 +1276,32 @@ func singleBucket(t *testing.T, buckets []autoimport.BucketStats) autoimport.Buc } return buckets[0] } + +func TestUnsavedExportAfterAutoImportsComputed(t *testing.T) { + t.Parallel() + root := "/home/src/proj" + files := map[string]any{ + root + "/exporter.ts": "", + root + "/tsconfig.json": `{"compilerOptions":{"module":"esnext","moduleResolution":"bundler","strict":true,"noEmit":true},"include":["*.ts"]}`, + } + session, _ := projecttestutil.Setup(files) + t.Cleanup(session.Close) + ctx := context.Background() + importer := lsproto.DocumentUri("file://" + root + "/importer.ts") + exporter := lsproto.DocumentUri("file://" + root + "/exporter.ts") + + session.DidOpenFile(ctx, importer, 1, "export {};", lsproto.LanguageKindTypeScript) + _, err := session.GetCurrentLanguageServiceWithAutoImports(ctx, importer) + assert.NilError(t, err) + session.DidCloseFile(ctx, importer) + + session.DidOpenFile(ctx, exporter, 1, "export function unsavedFn() {}", lsproto.LanguageKindTypeScript) + _, err = session.GetLanguageService(ctx, exporter) + assert.NilError(t, err) + session.DidOpenFile(ctx, importer, 1, "unsavedFn;", lsproto.LanguageKindTypeScript) + _, err = session.GetCurrentLanguageServiceWithAutoImports(ctx, importer) + assert.NilError(t, err) + stats := autoImportStats(t, session) + bucket := singleBucket(t, stats.ProjectBuckets) + assert.Assert(t, bucket.ExportCount > 0, "exporter.ts exports should be indexed: %+v", bucket) +} diff --git a/tsc/internal/ls/codeactions_fixclassincorrectlyimplementsinterface.go b/tsc/internal/ls/codeactions_fixclassincorrectlyimplementsinterface.go index b91cc1b1c44b4..859f9e17d3710 100644 --- a/tsc/internal/ls/codeactions_fixclassincorrectlyimplementsinterface.go +++ b/tsc/internal/ls/codeactions_fixclassincorrectlyimplementsinterface.go @@ -190,10 +190,10 @@ func getMissingMembers(typeChecker *checker.Checker, classDeclaration *ast.Node, if symbol == nil { continue } - if classMembers != nil && classMembers[symbol.Name()] != nil { + if classMembers != nil && classMembers.Get(symbol.Name()) != nil { continue } - if inheritedMembers[symbol.Name()] != nil || seenMembers[symbol.Name()] != nil { + if inheritedMembers.Get(symbol.Name()) != nil || seenMembers[symbol.Name()] != nil { continue } flags := checker.GetDeclarationModifierFlagsFromSymbol(symbol) @@ -209,22 +209,22 @@ func getMissingMembers(typeChecker *checker.Checker, classDeclaration *ast.Node, func getInheritedMembers(typeChecker *checker.Checker, classDeclaration *ast.Node) ast.SymbolTable { typeNode := ast.GetClassExtendsHeritageElement(classDeclaration) if typeNode == nil { - return ast.SymbolTable{} + return ast.NewSymbolTable() } baseType := typeChecker.GetTypeAtLocation(typeNode.AsNode()) if baseType == nil { - return ast.SymbolTable{} + return ast.NewSymbolTable() } - inheritedMembers := make(ast.SymbolTable) + inheritedMembers := ast.NewSymbolTable() for _, symbol := range typeChecker.GetPropertiesOfType(baseType) { if symbol == nil { continue } flags := checker.GetDeclarationModifierFlagsFromSymbol(symbol) if flags&ast.ModifierFlagsPrivate == 0 { - inheritedMembers[symbol.Name()] = symbol + inheritedMembers.Set(symbol.Name(), symbol) } } return inheritedMembers diff --git a/tsc/internal/ls/completions.go b/tsc/internal/ls/completions.go index 079360c8f495c..d0853a12bbcf8 100644 --- a/tsc/internal/ls/completions.go +++ b/tsc/internal/ls/completions.go @@ -1408,9 +1408,9 @@ func (l *LanguageService) getCompletionData( if localSymbol != nil { localExports = localSymbol.Exports() } - for name, symbol := range localsContainer.Locals() { + for name, symbol := range localsContainer.Locals().All() { symbols = append(symbols, symbol) - if _, ok := localExports[name]; ok { + if _, ok := localExports.Lookup(name); ok { symbolId := ast.GetSymbolId(symbol) symbolToSortTextMap[symbolId] = SortTextOptionalMember } diff --git a/tsc/internal/ls/definition.go b/tsc/internal/ls/definition.go index eb1761a6d98d2..0a1279d767c65 100644 --- a/tsc/internal/ls/definition.go +++ b/tsc/internal/ls/definition.go @@ -352,7 +352,7 @@ func getDeclarationsFromLocation(c *checker.Checker, node *ast.Node) []*ast.Node node = getDeclarationNameForKeyword(node) if symbol := c.GetSymbolAtLocation(node); symbol != nil { if symbol.Flags()&ast.SymbolFlagsClass != 0 && symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsVariable) == 0 && node.Kind == ast.KindConstructorKeyword { - if constructor := symbol.Members()[ast.InternalSymbolNameConstructor]; constructor != nil { + if constructor := symbol.Members().Get(ast.InternalSymbolNameConstructor); constructor != nil { symbol = constructor } } diff --git a/tsc/internal/ls/findallreferences.go b/tsc/internal/ls/findallreferences.go index 6e40e4aa1211c..ec77cebfc46fb 100644 --- a/tsc/internal/ls/findallreferences.go +++ b/tsc/internal/ls/findallreferences.go @@ -1408,7 +1408,7 @@ func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ct } else { return nil } - exportEquals := symbol.Exports()[ast.InternalSymbolNameExportEquals] + exportEquals := symbol.Exports().Get(ast.InternalSymbolNameExportEquals) // If exportEquals != nil, we're about to add references to `import("mod")` anyway, so don't double-count them. moduleReferences := l.getReferencedSymbolsForModule(checker, program, symbol, exportEquals != nil, sourceFiles, sourceFilesSet) if exportEquals == nil || exportEquals.Flags()&ast.SymbolFlagsAlias == 0 || !sourceFilesSet.Has(moduleSourceFileName) { @@ -1798,7 +1798,7 @@ func (l *LanguageService) getReferencedSymbolsForModule(checker *checker.Checker } // Handle export equals declarations - exported := symbol.Exports()[ast.InternalSymbolNameExportEquals] + exported := symbol.Exports().Get(ast.InternalSymbolNameExportEquals) if exported != nil && len(exported.Declarations()) > 0 { for _, decl := range exported.Declarations() { sourceFile := ast.GetSourceFileOfNode(decl) @@ -2041,7 +2041,7 @@ func getClassConstructorSymbol(classSymbol *ast.Symbol) *ast.Symbol { if classSymbol.Members() == nil { return nil } - return classSymbol.Members()[ast.InternalSymbolNameConstructor] + return classSymbol.Members().Get(ast.InternalSymbolNameConstructor) } func hasOwnConstructor(classDeclaration *ast.ClassLikeDeclaration) bool { @@ -2061,7 +2061,7 @@ func findOwnConstructorReferences(classSymbol *ast.Symbol, sourceFile *ast.Sourc } if classSymbol.Exports() != nil { - for _, member := range classSymbol.Exports() { + for _, member := range classSymbol.Exports().All() { decl := member.ValueDeclaration() if decl != nil && decl.Kind == ast.KindMethodDeclaration { body := decl.Body() diff --git a/tsc/internal/modulespecifiers/specifiers.go b/tsc/internal/modulespecifiers/specifiers.go index 261c26755a640..2518d4c00ca2f 100644 --- a/tsc/internal/modulespecifiers/specifiers.go +++ b/tsc/internal/modulespecifiers/specifiers.go @@ -136,7 +136,7 @@ func tryGetModuleNameFromAmbientModule(moduleSymbol *ast.Symbol, checker Checker continue } - sym, ok := possibleContainer.Symbol().Exports()[ast.InternalSymbolNameExportEquals] + sym, ok := possibleContainer.Symbol().Exports().Lookup(ast.InternalSymbolNameExportEquals) if !ok || sym == nil { continue } diff --git a/tsc/internal/packagejson/exportsorimports.go b/tsc/internal/packagejson/exportsorimports.go index 7add2e9f4aaac..85125e0bbff11 100644 --- a/tsc/internal/packagejson/exportsorimports.go +++ b/tsc/internal/packagejson/exportsorimports.go @@ -23,7 +23,7 @@ type ExportsOrImports struct { var _ json.UnmarshalerFrom = (*ExportsOrImports)(nil) func (e *ExportsOrImports) UnmarshalJSONFrom(dec *json.Decoder) error { - return e.JSONValue.unmarshalJSONValueFrom[ExportsOrImports](dec) + return unmarshalJSONValueFrom[ExportsOrImports](&e.JSONValue, dec) } func (e ExportsOrImports) AsObject() *collections.OrderedMap[string, ExportsOrImports] { diff --git a/tsc/internal/packagejson/jsonvalue.go b/tsc/internal/packagejson/jsonvalue.go index ef88e9b07c426..700e2bc510684 100644 --- a/tsc/internal/packagejson/jsonvalue.go +++ b/tsc/internal/packagejson/jsonvalue.go @@ -86,10 +86,10 @@ func (v JSONValue) AsString() string { var _ json.UnmarshalerFrom = (*JSONValue)(nil) func (v *JSONValue) UnmarshalJSONFrom(dec *json.Decoder) error { - return v.unmarshalJSONValueFrom[JSONValue](dec) + return unmarshalJSONValueFrom[JSONValue](v, dec) } -func (v *JSONValue) unmarshalJSONValueFrom[T any](dec *json.Decoder) error { +func unmarshalJSONValueFrom[T any](v *JSONValue, dec *json.Decoder) error { switch dec.PeekKind() { case 'n': // json.Null.Kind() if _, err := dec.ReadToken(); err != nil { diff --git a/tsc/internal/parser/parser.go b/tsc/internal/parser/parser.go index c670d64ad21c2..d60e049d6f987 100644 --- a/tsc/internal/parser/parser.go +++ b/tsc/internal/parser/parser.go @@ -4,6 +4,7 @@ import ( "slices" "strings" "sync" + "sync/atomic" "unicode/utf8" "github.com/microsoft/TypeScript/tsc/internal/ast" @@ -95,9 +96,13 @@ type Parser struct { jsdocTagCommentsPartsSpace []*ast.Node reparseList []*ast.Node - currentParent *ast.Node - setParentFromContext ast.Visitor - reparsedClones []*ast.Node + currentParent *ast.Node + setParentFromContext ast.Visitor + reparsedClones []*ast.Node + freeNodeLists [][]*ast.Node + lazyDts bool + seenImportTypeLiteral string + lazyLists []*ast.LazyNodeList } func newParser() *Parser { @@ -122,15 +127,58 @@ var parserPool = sync.Pool{ }, } +func ResetWasmPools() { + parserPool = sync.Pool{ + New: func() any { + return newParser() + }, + } +} + +var lazyDtsEnabled atomic.Bool + +func EnableLazyDts(enabled bool) { + lazyDtsEnabled.Store(enabled) +} + +func init() { + ast.SetLazyParseHook(reparseLazyList) + ast.SetLazyLookupHook(lookupLazyBinaryMember) +} + func getParser() *Parser { return parserPool.Get().(*Parser) } func putParser(p *Parser) { - *p = Parser{scanner: p.scanner, setParentFromContext: p.setParentFromContext} + p.factory.ResetCounts() + *p = Parser{ + scanner: p.scanner, + factory: p.factory, + nodeSliceArena: p.nodeSliceArena, + stringSliceArena: p.stringSliceArena, + setParentFromContext: p.setParentFromContext, + freeNodeLists: p.freeNodeLists, + } parserPool.Put(p) } +func (p *Parser) allocNodeList() []*ast.Node { + if n := len(p.freeNodeLists); n > 0 { + list := p.freeNodeLists[n-1] + p.freeNodeLists = p.freeNodeLists[:n-1] + return list[:0] + } + return make([]*ast.Node, 0, 16) +} + +func (p *Parser) freeNodeList(list []*ast.Node) { + if list != nil { + clear(list) + p.freeNodeLists = append(p.freeNodeLists, list[:0]) + } +} + func ParseSourceFile(opts ast.SourceFileParseOptions, sourceText string, scriptKind core.ScriptKind) *ast.SourceFile { p := getParser() defer putParser(p) @@ -431,6 +479,7 @@ func (p *Parser) parseSourceFileWorker() *ast.SourceFile { isDeclarationFile := p.opts.FileName.IsDeclarationFile() if isDeclarationFile { p.contextFlags |= ast.NodeFlagsAmbient + p.lazyDts = lazyDtsEnabled.Load() } pos := p.nodePos() statements := p.parseListIndex(PCSourceElements, (*Parser).parseToplevelStatement) @@ -483,6 +532,10 @@ func (p *Parser) finishSourceFile(result *ast.SourceFile, isDeclarationFile bool slices.SortFunc(p.reparsedClones, ast.CompareNodePositions) result.ReparsedClones = slices.Clone(p.reparsedClones) ast.SetExternalModuleIndicator(result, p.opts.ExternalModuleIndicatorOptions) + if len(p.lazyLists) > 0 { + result.LazyLists = slices.Clone(p.lazyLists) + p.lazyLists = nil + } } func (p *Parser) createJSDocCache() map[*ast.Node][]*ast.Node { @@ -610,14 +663,23 @@ func (p *Parser) reparseTopLevelAwait(sourceFile *ast.SourceFile) *ast.Node { } func (p *Parser) parseListIndex(kind ParsingContext, parseElement func(p *Parser, index int) *ast.Node) []*ast.Node { + return p.parseListWorker(kind, nil, parseElement) +} + +func (p *Parser) parseListWorker(kind ParsingContext, parseElement func(p *Parser) *ast.Node, parseElementIndex func(p *Parser, index int) *ast.Node) []*ast.Node { saveParsingContexts := p.parsingContexts p.parsingContexts |= 1 << kind outerReparseList := p.reparseList p.reparseList = nil - list := make([]*ast.Node, 0, 16) + list := p.allocNodeList() for i := 0; !p.isListTerminator(kind); i++ { if p.isListElement(kind, false /*inErrorRecovery*/) { - elt := parseElement(p, len(list)) + var elt *ast.Node + if parseElement != nil { + elt = parseElement(p) + } else { + elt = parseElementIndex(p, len(list)) + } if len(p.reparseList) != 0 { for _, e := range p.reparseList { // Propagate @typedef type alias declarations outwards to a context that permits them. @@ -638,27 +700,392 @@ func (p *Parser) parseListIndex(kind ParsingContext, parseElement func(p *Parser } p.reparseList = outerReparseList p.parsingContexts = saveParsingContexts - return p.nodeSliceArena.Clone(list) + res := p.nodeSliceArena.Clone(list) + p.freeNodeList(list) + return res } func (p *Parser) parseList(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList { pos := p.nodePos() - nodes := p.parseListIndex(kind, func(p *Parser, _ int) *ast.Node { return parseElement(p) }) + nodes := p.parseListWorker(kind, parseElement, nil) return p.newNodeList(core.NewTextRange(pos, p.nodePos()), nodes) } +const lazyListMaxText = 1 << 28 + +func (p *Parser) parseMemberList(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList { + if p.lazyDts && p.contextFlags&ast.NodeFlagsJSDoc == 0 && p.token != ast.KindCloseBraceToken { + return p.parseMemberListLazily(kind, parseElement) + } + return p.parseList(kind, parseElement) +} + +func (p *Parser) parseMemberListLazily(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList { + if !p.hasParseError && + !p.scanner.HasPrecedingJSDocWithSeeOrLink() && + !p.scanner.HasPrecedingJSDocWithDeprecatedTag() { + if lastTokenEnd, ok := scanBalancedBraces(p.sourceText, p.scanner.TokenStart(), kind == PCClassMembers); ok { + startPos := p.nodePos() + contextFlags := p.contextFlags + parsingContexts := p.parsingContexts + beforeCommentDirectives := len(p.scanner.CommentDirectives()) + markState := p.mark() + p.scanner.ResetTokenState(lastTokenEnd) + p.nextToken() + if p.token == ast.KindCloseBraceToken && + !p.hasParseError && + len(p.scanner.CommentDirectives()) == beforeCommentDirectives && + !p.scanner.HasPrecedingJSDocWithSeeOrLink() && + !p.scanner.HasPrecedingJSDocWithDeprecatedTag() { + loc := core.NewTextRange(startPos, p.nodePos()) + lazy := ast.NewLazyNodeList(loc, uint8(kind), contextFlags, uint32(parsingContexts)) + p.lazyLists = append(p.lazyLists, lazy) + return &lazy.NodeList + } + p.rewind(markState) + } + } + return p.parseList(kind, parseElement) +} + +func scanBalancedBraces(text string, startPos int, isClass bool) (int, bool) { + maxEnd := min(len(text), startPos+lazyListMaxText) + i := startPos + depth := 0 + parenDepth := 0 + bracketDepth := 0 + lastTokenEnd := startPos + for i < maxEnd { + c := text[i] + switch c { + case ' ', '\t', '\r', '\n': + i++ + case '}': + if depth == 0 { + if parenDepth != 0 || bracketDepth != 0 || lastTokenEnd == startPos { + return 0, false + } + return lastTokenEnd, true + } + depth-- + i++ + lastTokenEnd = i + case '{': + if isClass && depth == 0 && parenDepth == 0 && bracketDepth == 0 { + return 0, false + } + depth++ + i++ + lastTokenEnd = i + case '(': + parenDepth++ + i++ + lastTokenEnd = i + case ')': + if parenDepth == 0 { + return 0, false + } + parenDepth-- + i++ + lastTokenEnd = i + case '[': + bracketDepth++ + i++ + lastTokenEnd = i + case ']': + if bracketDepth == 0 { + return 0, false + } + bracketDepth-- + i++ + lastTokenEnd = i + case '"', '\'': + quote := c + i++ + for i < maxEnd { + ch := text[i] + if ch == '\\' { + i += 2 + continue + } + if ch == '\n' || ch == '\r' { + return 0, false + } + i++ + if ch == quote { + break + } + } + if i > maxEnd || text[i-1] != quote { + return 0, false + } + lastTokenEnd = i + case '`': + i++ + for i < maxEnd { + ch := text[i] + if ch == '\\' { + i += 2 + continue + } + if ch == '$' && i+1 < maxEnd && text[i+1] == '{' { + return 0, false + } + i++ + if ch == '`' { + break + } + } + if i > maxEnd || text[i-1] != '`' { + return 0, false + } + lastTokenEnd = i + case '/': + if i+1 >= maxEnd { + return 0, false + } + next := text[i+1] + if next == '/' { + i += 2 + for i < maxEnd && text[i] != '\n' { + if text[i] == '@' || text[i] == '/' { + return 0, false + } + i++ + } + } else if next == '*' { + i += 2 + closed := false + for i+1 < maxEnd { + if text[i] == '@' { + return 0, false + } + if text[i] == '*' && text[i+1] == '/' { + i += 2 + closed = true + break + } + i++ + } + if !closed { + return 0, false + } + } else { + return 0, false + } + case '@', '#': + return 0, false + case '=': + if i+1 < maxEnd && text[i+1] == '>' { + i += 2 + lastTokenEnd = i + continue + } + if isClass && depth == 0 { + return 0, false + } + i++ + lastTokenEnd = i + default: + if c >= 0x80 { + return 0, false + } + if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' { + wordStart := i + i++ + for i < maxEnd { + ch := text[i] + if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '$' { + i++ + } else if ch >= 0x80 { + return 0, false + } else { + break + } + } + word := text[wordStart:i] + switch len(word) { + case 4: + if word == "this" || word == "void" { + return 0, false + } + case 5: + if word == "super" || word == "infer" || word == "async" || word == "await" || word == "yield" || word == "class" { + return 0, false + } + case 6: + if word == "import" || word == "delete" { + return 0, false + } + case 8: + if word == "function" { + return 0, false + } + case 9: + if word == "satisfies" { + return 0, false + } + } + lastTokenEnd = i + } else if c >= '0' && c <= '9' { + i++ + for i < maxEnd { + ch := text[i] + if (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '.' { + i++ + } else { + break + } + } + lastTokenEnd = i + } else { + i++ + lastTokenEnd = i + } + } + } + return 0, false +} + +func reparseLazyList(lazy *ast.LazyNodeList, file *ast.SourceFile, allowNestedLazy bool) []*ast.Node { + p := getParser() + defer putParser(p) + p.initializeState(file.ParseOptions(), file.Text(), file.ScriptKind) + p.lazyDts = allowNestedLazy + p.scanner.ResetTokenState(lazy.Pos()) + p.nextToken() + p.contextFlags = lazy.ContextFlags + p.parsingContexts = ParsingContexts(lazy.ParsingContexts) + var nodes []*ast.Node + if lazy.ParsingContext == ast.LazyParsingContextTypeAliasRHS { + var typeNode *ast.Node + if p.token == ast.KindIntrinsicKeyword && p.lookAhead((*Parser).nextIsNotDot) { + typeNode = p.parseKeywordTypeNode() + } else { + typeNode = p.parseType() + } + if typeNode != nil { + typeNode.Parent = lazy.Owner + nodes = []*ast.Node{typeNode} + } + } else { + var list *ast.NodeList + if ParsingContext(lazy.ParsingContext) == PCClassMembers { + list = p.parseList(PCClassMembers, (*Parser).parseClassElement) + } else { + list = p.parseList(PCTypeMembers, (*Parser).parseTypeMember) + } + owner := lazy.Owner + nodes = list.Nodes + for _, m := range nodes { + m.Parent = owner + } + } + if len(p.lazyLists) > 0 { + for _, nested := range p.lazyLists { + nested.File = file + } + lazy.SetNestedLazyLists(slices.Clone(p.lazyLists)) + p.lazyLists = nil + } + return nodes +} + +func lookupLazyBinaryMember(lazy *ast.LazyNodeList, file *ast.SourceFile, name string) *ast.Node { + text := file.Text()[lazy.Pos():lazy.End()] + if len(text) < 8 || text[0] != '\n' { + return nil + } + i := 1 + for i < len(text) && i < 16 && text[i] == ' ' { + i++ + } + if i == 1 || i >= len(text) || text[i] != '\'' { + return nil + } + prefixLen := i + 1 + prefix := text[:prefixLen] + kStart := prefixLen + kEnd := kStart + for kEnd < len(text) && kEnd < kStart+64 && (text[kEnd] == '0' || text[kEnd] == '1') { + kEnd++ + } + keyLen := kEnd - kStart + if keyLen == 0 || len(name) != keyLen || kEnd+2 > len(text) || text[kEnd] != '\'' || text[kEnd+1] != ':' { + return nil + } + for j := range len(name) { + if name[j] != '0' && name[j] != '1' { + return nil + } + } + lo := 0 + hi := len(text) + foundPos := -1 + for lo < hi { + mid := int(uint(lo+hi) >> 1) + rel := strings.Index(text[mid:], prefix) + if rel < 0 || mid+rel >= hi { + if mid == lo { + break + } + hi = mid + continue + } + entryPos := mid + rel + candStart := entryPos + prefixLen + candEnd := candStart + keyLen + if candEnd+2 > len(text) || text[candEnd] != '\'' || text[candEnd+1] != ':' { + break + } + cand := text[candStart:candEnd] + if cand == name { + foundPos = entryPos + break + } + if cand < name { + lo = candEnd + 2 + } else { + hi = entryPos + } + } + if foundPos < 0 { + return nil + } + p := getParser() + defer putParser(p) + p.initializeState(file.ParseOptions(), file.Text(), file.ScriptKind) + p.lazyDts = true + p.scanner.ResetTokenState(lazy.Pos() + foundPos) + p.nextToken() + p.contextFlags = lazy.ContextFlags + p.parsingContexts = ParsingContexts(lazy.ParsingContexts) + node := p.parseTypeMember() + if node != nil { + node.Parent = lazy.Owner + } + if len(p.lazyLists) > 0 { + for _, nested := range p.lazyLists { + nested.File = file + } + p.lazyLists = nil + } + return node +} + // Return a non-nil (but possibly empty) slice if parsing was successful, or nil if parseElement returned nil func (p *Parser) parseDelimitedList(kind ParsingContext, parseElement func(p *Parser) *ast.Node) *ast.NodeList { pos := p.nodePos() saveParsingContexts := p.parsingContexts p.parsingContexts |= 1 << kind - list := make([]*ast.Node, 0, 16) + list := p.allocNodeList() for { if p.isListElement(kind, false /*inErrorRecovery*/) { startPos := p.nodePos() element := parseElement(p) if element == nil { p.parsingContexts = saveParsingContexts + p.freeNodeList(list) // Return nil to indicate parseElement failed return nil } @@ -702,7 +1129,9 @@ func (p *Parser) parseDelimitedList(kind ParsingContext, parseElement func(p *Pa } } p.parsingContexts = saveParsingContexts - return p.newNodeList(core.NewTextRange(pos, p.nodePos()), p.nodeSliceArena.Clone(list)) + res := p.newNodeList(core.NewTextRange(pos, p.nodePos()), p.nodeSliceArena.Clone(list)) + p.freeNodeList(list) + return res } // Return a non-nil (but possibly empty) NodeList if parsing was successful, a missing NodeList if the opening @@ -1761,7 +2190,7 @@ func (p *Parser) parseClassDeclarationOrExpression(pos int, jsdoc jsdocScannerIn if p.parseExpected(ast.KindOpenBraceToken) { // ClassTail[Yield,Await] : (Modified) See 14.5 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } - members = p.parseList(PCClassMembers, (*Parser).parseClassElement) + members = p.parseMemberList(PCClassMembers, (*Parser).parseClassElement) p.parseExpected(ast.KindCloseBraceToken) } else { members = p.createMissingList() @@ -1776,7 +2205,8 @@ func (p *Parser) parseClassDeclarationOrExpression(pos int, jsdoc jsdocScannerIn } else { result = p.factory.NewClassExpression(modifiers, name, typeParameters, heritageClauses, members) } - p.finishNode(result, pos) + ast.SetLazyOwner(members, result) + result = p.finishNode(result, pos) p.withJSDoc(result, jsdoc) if result.Flags&ast.NodeFlagsJavaScriptFile != 0 { p.checkJSSyntax(result) @@ -2133,6 +2563,7 @@ func (p *Parser) parseInterfaceDeclaration(pos int, jsdoc jsdocScannerInfo, modi heritageClauses := p.parseHeritageClauses(true /*isInterface*/) members := p.parseObjectTypeMembers() result := p.finishNode(p.factory.NewInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members), pos) + ast.SetLazyOwner(members, result) p.withJSDoc(result, jsdoc) p.checkJSSyntax(result) return result @@ -2147,18 +2578,398 @@ func (p *Parser) parseTypeAliasDeclaration(pos int, jsdoc jsdocScannerInfo, modi typeParameters := p.parseTypeParameters() p.parseExpected(ast.KindEqualsToken) var typeNode *ast.TypeNode - if p.token == ast.KindIntrinsicKeyword && p.lookAhead((*Parser).nextIsNotDot) { - typeNode = p.parseKeywordTypeNode() - } else { - typeNode = p.parseType() + if p.lazyDts && p.contextFlags&ast.NodeFlagsJSDoc == 0 && !p.hasParseError && + !p.scanner.HasPrecedingJSDocWithSeeOrLink() && !p.scanner.HasPrecedingJSDocWithDeprecatedTag() { + typeNode = p.parseTypeAliasRHSLazily() + } + if typeNode == nil { + if p.token == ast.KindIntrinsicKeyword && p.lookAhead((*Parser).nextIsNotDot) { + typeNode = p.parseKeywordTypeNode() + } else { + typeNode = p.parseType() + } } p.parseSemicolon() result := p.finishNode(p.factory.NewTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), pos) + ast.SetLazyTypeOwner(typeNode, result) p.withJSDoc(result, jsdoc) p.checkJSSyntax(result) return result } +func (p *Parser) parseTypeAliasRHSLazily() *ast.TypeNode { + if lastTokenEnd, ok := scanLazyTypeAliasRHS(p.sourceText, p.scanner.TokenStart(), p.seenImportTypeLiteral); ok { + startPos := p.nodePos() + contextFlags := p.contextFlags + parsingContexts := p.parsingContexts + beforeCommentDirectives := len(p.scanner.CommentDirectives()) + markState := p.mark() + p.scanner.ResetTokenState(lastTokenEnd) + p.nextToken() + if p.canParseSemicolon() && + !p.hasParseError && + len(p.scanner.CommentDirectives()) == beforeCommentDirectives && + !p.scanner.HasPrecedingJSDocWithSeeOrLink() && + !p.scanner.HasPrecedingJSDocWithDeprecatedTag() { + loc := core.NewTextRange(startPos, p.nodePos()) + lazy := ast.NewLazyTypeAliasRHS(loc, contextFlags, uint32(parsingContexts)) + p.lazyLists = append(p.lazyLists, lazy) + return &lazy.Placeholder + } + p.rewind(markState) + } + return nil +} + +func scanLazyTypeAliasRHS(text string, startPos int, seenImportLiteral string) (int, bool) { + maxEnd := min(len(text), startPos+lazyListMaxText) + i := startPos + braceDepth := 0 + parenDepth := 0 + bracketDepth := 0 + angleDepth := 0 + lastTokenEnd := startPos + var lastSigChar byte + var lastWord string + for i < maxEnd { + c := text[i] + switch c { + case ' ', '\t', '\r': + i++ + case '\n': + i++ + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 && lastTokenEnd > startPos { + if lastSigChar != '=' && lastSigChar != '?' && lastSigChar != ':' && + lastSigChar != '|' && lastSigChar != '&' && lastSigChar != ',' && + lastWord != "extends" && lastWord != "keyof" && lastWord != "typeof" && + lastWord != "infer" && lastWord != "readonly" && lastWord != "unique" && + lastWord != "in" && lastWord != "out" && lastWord != "is" && lastWord != "asserts" { + if isStmtStartAfterNewline(text, i, maxEnd) { + return lastTokenEnd, true + } + } + } + case ';': + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + if lastTokenEnd == startPos { + return 0, false + } + return lastTokenEnd, true + } + i++ + lastTokenEnd = i + case '{': + braceDepth++ + i++ + lastTokenEnd = i + case '}': + if braceDepth == 0 { + if parenDepth != 0 || bracketDepth != 0 || angleDepth != 0 || lastTokenEnd == startPos { + return 0, false + } + return lastTokenEnd, true + } + braceDepth-- + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = '}' + lastWord = "" + } + case '(': + parenDepth++ + i++ + lastTokenEnd = i + case ')': + if parenDepth == 0 { + return 0, false + } + parenDepth-- + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = ')' + lastWord = "" + } + case '[': + bracketDepth++ + i++ + lastTokenEnd = i + case ']': + if bracketDepth == 0 { + return 0, false + } + bracketDepth-- + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = ']' + lastWord = "" + } + case '<': + angleDepth++ + i++ + lastTokenEnd = i + case '>': + if angleDepth == 0 { + return 0, false + } + angleDepth-- + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = '>' + lastWord = "" + } + case '\'', '"': + quote := c + i++ + for i < maxEnd { + ch := text[i] + if ch == '\\' { + i += 2 + continue + } + if ch == '\n' || ch == '\r' { + return 0, false + } + i++ + if ch == quote { + break + } + } + if i > maxEnd || text[i-1] != quote { + return 0, false + } + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = quote + lastWord = "" + } + case '`': + i++ + tplBrace := 0 + for i < maxEnd { + ch := text[i] + if ch == '\\' { + i += 2 + continue + } + if ch == '$' && i+1 < maxEnd && text[i+1] == '{' { + tplBrace++ + i += 2 + continue + } + if tplBrace > 0 { + if ch == '{' { + tplBrace++ + } else if ch == '}' { + tplBrace-- + } + i++ + continue + } + i++ + if ch == '`' { + break + } + } + if i > maxEnd || text[i-1] != '`' || tplBrace != 0 { + return 0, false + } + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = '`' + lastWord = "" + } + case '/': + if i+1 >= maxEnd { + return 0, false + } + next := text[i+1] + if next == '/' { + i += 2 + for i < maxEnd && text[i] != '\n' { + if text[i] == '@' || text[i] == '/' { + return 0, false + } + i++ + } + } else if next == '*' { + i += 2 + closed := false + for i+1 < maxEnd { + if text[i] == '@' { + return 0, false + } + if text[i] == '*' && text[i+1] == '/' { + i += 2 + closed = true + break + } + i++ + } + if !closed { + return 0, false + } + } else { + return 0, false + } + case '@', '#': + return 0, false + case '=': + if i+1 < maxEnd && text[i+1] == '>' { + i += 2 + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = '=' + lastWord = "" + } + continue + } + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = '=' + lastWord = "" + } + default: + if c >= 0x80 { + return 0, false + } + if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' { + wordStart := i + i++ + for i < maxEnd { + ch := text[i] + if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '$' { + i++ + } else if ch >= 0x80 { + return 0, false + } else { + break + } + } + word := text[wordStart:i] + switch word { + case "this", "void", "super", "delete", "async", "await", "yield", "class", "function", "satisfies": + return 0, false + case "import": + if seenImportLiteral == "" || !matchesSeenImportLiteral(text, i, maxEnd, seenImportLiteral) { + return 0, false + } + } + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = c + lastWord = word + } + } else { + i++ + lastTokenEnd = i + if braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 { + lastSigChar = c + lastWord = "" + } + } + } + } + if i == len(text) && braceDepth == 0 && parenDepth == 0 && bracketDepth == 0 && angleDepth == 0 && lastTokenEnd > startPos { + return lastTokenEnd, true + } + return 0, false +} + +func matchesSeenImportLiteral(text string, pos int, maxEnd int, seen string) bool { + // Check for `('seen')` or `("seen")` immediately following `import` + need := len(seen) + 4 + if pos+need > maxEnd || text[pos] != '(' { + return false + } + q := text[pos+1] + if q != '\'' && q != '"' { + return false + } + return text[pos+2:pos+2+len(seen)] == seen && text[pos+2+len(seen)] == q && text[pos+3+len(seen)] == ')' +} + +func isStmtStartAfterNewline(text string, pos int, maxEnd int) bool { + i := pos + for i < maxEnd { + c := text[i] + if c == ' ' || c == '\t' || c == '\r' || c == '\n' { + i++ + continue + } + if c == '/' && i+1 < maxEnd { + next := text[i+1] + if next == '/' { + if i+2 < maxEnd && (text[i+2] == '@' || text[i+2] == '/') { + return false + } + i += 2 + for i < maxEnd && text[i] != '\n' { + if text[i] == '@' { + return false + } + i++ + } + continue + } + if next == '*' { + i += 2 + closed := false + for i+1 < maxEnd { + if text[i] == '@' { + return false + } + if text[i] == '*' && text[i+1] == '/' { + i += 2 + closed = true + break + } + i++ + } + if !closed { + return false + } + continue + } + } + break + } + if i >= len(text) { + return true + } + if i >= maxEnd { + return false + } + c := text[i] + if c == '}' || c == ';' { + return true + } + if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') { + start := i + i++ + for i < maxEnd { + ch := text[i] + if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '$' { + i++ + } else { + break + } + } + if i < maxEnd && (text[i] == ' ' || text[i] == '\t' || text[i] == '{' || text[i] == '*') { + switch text[start:i] { + case "type", "export", "declare", "interface", "const", "let", "var", + "function", "class", "enum", "namespace", "module", "global", "import", "abstract": + return true + } + } + } + return false +} + func (p *Parser) nextIsNotDot() bool { return p.nextToken() != ast.KindDotToken } @@ -2721,12 +3532,13 @@ func (p *Parser) parseUnionOrIntersectionType(operator ast.Kind, parseConstituen typeNode = parseConstituentType(p) } if p.token == operator || hasLeadingOperator { - types := make([]*ast.Node, 1, 8) - types[0] = typeNode + types := append(p.allocNodeList(), typeNode) for p.parseOptional(operator) { types = append(types, p.parseFunctionOrConstructorTypeToError(isUnionType, parseConstituentType)) } - typeNode = p.createUnionOrIntersectionTypeNode(operator, p.newNodeList(core.NewTextRange(pos, p.nodePos()), p.nodeSliceArena.Clone(types))) + cloned := p.nodeSliceArena.Clone(types) + p.freeNodeList(types) + typeNode = p.createUnionOrIntersectionTypeNode(operator, p.newNodeList(core.NewTextRange(pos, p.nodePos()), cloned)) p.finishNode(typeNode, pos) } return typeNode @@ -3131,6 +3943,9 @@ func (p *Parser) parseImportType() *ast.Node { } } p.parseExpected(ast.KindCloseParenToken) + if attributes == nil && ast.IsLiteralTypeNode(typeNode) && ast.IsStringLiteral(typeNode.AsLiteralTypeNode().Literal) { + p.seenImportTypeLiteral = typeNode.AsLiteralTypeNode().Literal.Text() + } var qualifier *ast.Node if p.parseOptional(ast.KindDotToken) { qualifier = p.parseEntityNameOfTypeReference() @@ -3671,13 +4486,15 @@ func (p *Parser) parsePropertyOrMethodSignature(pos int, jsdoc jsdocScannerInfo, func (p *Parser) parseTypeLiteral() *ast.Node { pos := p.nodePos() - result := p.finishNode(p.factory.NewTypeLiteralNode(p.parseObjectTypeMembers()), pos) + members := p.parseObjectTypeMembers() + result := p.finishNode(p.factory.NewTypeLiteralNode(members), pos) + ast.SetLazyOwner(members, result) return result } func (p *Parser) parseObjectTypeMembers() *ast.NodeList { if p.parseExpected(ast.KindOpenBraceToken) { - members := p.parseList(PCTypeMembers, (*Parser).parseTypeMember) + members := p.parseMemberList(PCTypeMembers, (*Parser).parseTypeMember) p.parseExpected(ast.KindCloseBraceToken) return members } @@ -3940,10 +4757,13 @@ func (p *Parser) parseModifiersEx(allowDecorators bool, permitConstAsModifier bo // It is illegal to have both leadingDecorators and trailingDecorators, but we will report that as a grammar check in the checker. // parse leading decorators pos := p.nodePos() - list := make([]*ast.Node, 0, 16) + var list []*ast.Node for { if allowDecorators && p.token == ast.KindAtToken && !hasTrailingModifier { decorator := p.parseDecorator() + if list == nil { + list = p.allocNodeList() + } list = append(list, decorator) if hasLeadingModifier { hasTrailingDecorator = true @@ -3956,6 +4776,9 @@ func (p *Parser) parseModifiersEx(allowDecorators bool, permitConstAsModifier bo if modifier.Kind == ast.KindStaticKeyword { hasStaticModifier = true } + if list == nil { + list = p.allocNodeList() + } list = append(list, modifier) if hasTrailingDecorator { hasTrailingModifier = true @@ -3965,7 +4788,9 @@ func (p *Parser) parseModifiersEx(allowDecorators bool, permitConstAsModifier bo } } if len(list) != 0 { - return p.newModifierList(core.NewTextRange(pos, p.nodePos()), p.nodeSliceArena.Clone(list)) + res := p.newModifierList(core.NewTextRange(pos, p.nodePos()), p.nodeSliceArena.Clone(list)) + p.freeNodeList(list) + return res } return nil } diff --git a/tsc/internal/printer/namegenerator.go b/tsc/internal/printer/namegenerator.go index 81078f087a3bd..efcec92f05e0b 100644 --- a/tsc/internal/printer/namegenerator.go +++ b/tsc/internal/printer/namegenerator.go @@ -366,7 +366,7 @@ func isUniqueLocalName(name string, container *ast.Node) bool { locals := node.Locals() if locals != nil { // We conservatively include alias symbols to cover cases where they're emitted as locals - if local, ok := locals[name]; ok && local.Flags()&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue|ast.SymbolFlagsAlias) != 0 { + if local, ok := locals.Lookup(name); ok && local.Flags()&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue|ast.SymbolFlagsAlias) != 0 { return false } } diff --git a/tsc/internal/printer/printer.go b/tsc/internal/printer/printer.go index a27452d51472d..f5897eca19f5b 100644 --- a/tsc/internal/printer/printer.go +++ b/tsc/internal/printer/printer.go @@ -4735,8 +4735,10 @@ func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNod isNil := children == nil length := 0 + var childrenNodes []*ast.Node if !isNil { - length = len(children.Nodes) + childrenNodes = children.Slice() + length = len(childrenNodes) } if start < 0 { @@ -4783,7 +4785,7 @@ func (p *Printer) emitListRange(emit func(p *Printer, node *ast.Node), parentNod } else { end := min(start+count, length) - p.emitListItems(emit, parentNode, children.Nodes[start:end], format, p.hasTrailingComma(parentNode, children), children.Loc) + p.emitListItems(emit, parentNode, childrenNodes[start:end], format, p.hasTrailingComma(parentNode, children), children.Loc) } if p.OnAfterEmitNodeList != nil { @@ -5095,7 +5097,7 @@ func (p *Printer) Write(node *ast.Node, sourceFile *ast.SourceFile, writer EmitT } p.writer = writer p.writer.Clear() - if sourceFile != nil { + if sourceFile != nil && node == sourceFile.AsNode() { if grower, ok := p.writer.(interface{ Grow(n int) }); ok { grower.Grow(len(sourceFile.Text())) } diff --git a/tsc/internal/printer/singlelinestringwriter.go b/tsc/internal/printer/singlelinestringwriter.go index 5accd0f6d84b4..27668f46505f8 100644 --- a/tsc/internal/printer/singlelinestringwriter.go +++ b/tsc/internal/printer/singlelinestringwriter.go @@ -16,6 +16,14 @@ var singleLineStringWriterPool sync.Pool = sync.Pool{ }, } +func ResetWasmPools() { + singleLineStringWriterPool = sync.Pool{ + New: func() any { + return &singleLineStringWriter{} + }, + } +} + var _ EmitTextWriter = &singleLineStringWriter{} func GetSingleLineStringWriter() (EmitTextWriter, func()) { diff --git a/tsc/internal/scanner/scanner.go b/tsc/internal/scanner/scanner.go index 785fe50a4e7df..3d9934fa4fa99 100644 --- a/tsc/internal/scanner/scanner.go +++ b/tsc/internal/scanner/scanner.go @@ -198,6 +198,50 @@ var textToToken = func() map[string]ast.Kind { return m }() +type keywordEntry struct { + text string + kind ast.Kind +} + +var keywordTable = func() [512]keywordEntry { + var table [512]keywordEntry + for text, kind := range textToKeyword { + n := len(text) + k := (uint32(text[0]) | uint32(text[1])<<8 | uint32(text[n-1])<<16 | uint32(text[n>>1])<<24) ^ (uint32(n) * 0x9e3779b9) + idx := (k * 0xa2053) >> 23 + table[idx] = keywordEntry{text: text, kind: kind} + } + return table +}() + +var isASCIIIdentPart = func() [256]bool { + var table [256]bool + for b := byte('a'); b <= 'z'; b++ { + table[b] = true + } + for b := byte('A'); b <= 'Z'; b++ { + table[b] = true + } + for b := byte('0'); b <= '9'; b++ { + table[b] = true + } + table['_'] = true + table['$'] = true + return table +}() + +func lookupKeyword(str string) ast.Kind { + n := len(str) + if n >= 2 && n <= 11 && str[0] >= 'a' && str[0] <= 'z' { + k := (uint32(str[0]) | uint32(str[1])<<8 | uint32(str[n-1])<<16 | uint32(str[n>>1])<<24) ^ (uint32(n) * 0x9e3779b9) + entry := &keywordTable[(k*0xa2053)>>23] + if entry.text == str { + return entry.kind + } + } + return ast.KindUnknown +} + type ScannerState struct { pos int // Current position in text (and ending position of current token) fullStartPos int // Starting position of current token including preceding whitespace @@ -486,6 +530,12 @@ func (s *Scanner) Scan() ast.Kind { case '\t', '\v', '\f', ' ': s.pos++ if s.skipTrivia { + text := s.text + pos := s.pos + for pos < s.end && (text[pos] == ' ' || text[pos] == '\t') { + pos++ + } + s.pos = pos continue } for { @@ -1489,13 +1539,15 @@ func (s *Scanner) scanIdentifier(prefixLength int, variant identifierVariant) bo ch := s.char() // Fast path for simple ASCII identifiers if variant != identifierVariantJSX && (stringutil.IsASCIILetter(ch) || ch == '_' || ch == '$') { - s.pos++ - s.scanASCIIWhile(func(b byte) bool { - return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_' || b == '$' - }) + text := s.text + pos := s.pos + 1 + for pos < s.end && isASCIIIdentPart[text[pos]] { + pos++ + } + s.pos = pos ch = s.char() if ch < utf8.RuneSelf && ch != '\\' { - s.tokenValue = s.text[start:s.pos] + s.tokenValue = text[start:pos] return true } s.pos = identifierStart @@ -2212,11 +2264,8 @@ func (s *Scanner) scanInvalidCharacter() { } func GetIdentifierToken(str string) ast.Kind { - if len(str) >= 2 && len(str) <= 12 && str[0] >= 'a' && str[0] <= 'z' { - keyword := textToKeyword[str] - if keyword != ast.KindUnknown { - return keyword - } + if keyword := lookupKeyword(str); keyword != ast.KindUnknown { + return keyword } return ast.KindIdentifier } diff --git a/tsc/internal/scanner/utilities.go b/tsc/internal/scanner/utilities.go index 6a8ad06ef4ad7..04d26e9186db2 100644 --- a/tsc/internal/scanner/utilities.go +++ b/tsc/internal/scanner/utilities.go @@ -16,7 +16,7 @@ func tokenIsIdentifierOrKeyword(token ast.Kind) bool { } func IdentifierToKeywordKind(node *ast.Identifier) ast.Kind { - return textToKeyword[node.Text] + return lookupKeyword(node.Text) } func GetSourceTextOfNodeFromSourceFile(sourceFile *ast.SourceFile, node *ast.Node, includeTrivia bool) string { diff --git a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts index e24c8a5d7152b..a38f3cfd2c706 100644 --- a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts +++ b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts @@ -231,17 +231,18 @@ ${renderRangeTable("unicodeESNextIdentifierPart", partTable)} `; } +export const outputs = ["js_case_generated.go", "identifier_parts_generated.go"] as const; + export default async function generateUnicode(force = false, outDir = path.resolve(scriptDir, "..")) { outDir = path.resolve(outDir); const inputs = [ import.meta.filename, fileURLToPath(import.meta.resolve(`${PACKAGE}/package.json`)), ]; - const caseOutput = new GeneratedFile(path.join(outDir, "js_case_generated.go"), inputs); - const identifierOutput = new GeneratedFile(path.join(outDir, "identifier_parts_generated.go"), inputs); + const caseOutput = new GeneratedFile(path.join(outDir, outputs[0]), inputs); + const identifierOutput = new GeneratedFile(path.join(outDir, outputs[1]), inputs); const generatedFiles = [caseOutput, identifierOutput]; if (generatedFiles.every(file => file.isCurrent(force))) { - console.log("Unicode tables are up to date."); return; } diff --git a/tsc/internal/transformers/declarations/tracker.go b/tsc/internal/transformers/declarations/tracker.go index 14d0fcd5e5032..404c2ab9661c8 100644 --- a/tsc/internal/transformers/declarations/tracker.go +++ b/tsc/internal/transformers/declarations/tracker.go @@ -214,7 +214,7 @@ func (s *SymbolTrackerImpl) handleSymbolAccessibilityError(symbolAccessibilityRe // The checker should issue errors on unresolvable names, skip the declaration emit error for using a private/unreachable name for those } else if symbolAccessibilityResult.Accessibility != printer.SymbolAccessibilityNotResolved { // Report error - errorInfo := s.state.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult) + errorInfo := s.state.evalSymbolAccessibilityDiagnostic(symbolAccessibilityResult) if errorInfo != nil { info := *errorInfo diagNode := symbolAccessibilityResult.ErrorNode @@ -240,6 +240,8 @@ type SymbolTrackerSharedState struct { lateMarkedStatements []*ast.Node diagnostics []*ast.Diagnostic getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic + diagnosticNode *ast.Node + diagnosticForNodeName bool errorNameNode *ast.Node isolatedDeclarations bool stripInternal bool @@ -248,6 +250,25 @@ type SymbolTrackerSharedState struct { reportExpandoFunctionErrors func(node *ast.Node) } +func (s *SymbolTrackerSharedState) setDiagnosticNode(node *ast.Node, forNodeName bool) { + s.getSymbolAccessibilityDiagnostic = nil + s.diagnosticNode = node + s.diagnosticForNodeName = forNodeName +} + +func (s *SymbolTrackerSharedState) evalSymbolAccessibilityDiagnostic(res printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + if s.getSymbolAccessibilityDiagnostic != nil { + return s.getSymbolAccessibilityDiagnostic(res) + } + if s.diagnosticNode != nil { + if s.diagnosticForNodeName { + return createGetSymbolAccessibilityDiagnosticForNodeName(s.diagnosticNode)(res) + } + return createGetSymbolAccessibilityDiagnosticForNode(s.diagnosticNode)(res) + } + return throwDiagnostic(res) +} + func (s *SymbolTrackerSharedState) addDiagnostic(diag *ast.Diagnostic) { s.diagnostics = append(s.diagnostics, diag) } diff --git a/tsc/internal/transformers/declarations/transform.go b/tsc/internal/transformers/declarations/transform.go index 7e362d9fb1660..dde7c11a10a8c 100644 --- a/tsc/internal/transformers/declarations/transform.go +++ b/tsc/internal/transformers/declarations/transform.go @@ -355,7 +355,7 @@ func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast combinedStatements.Loc = statements.Loc // setTextRange if ast.IsExternalOrCommonJSModule(node) { if ast.IsInJSFile(node.AsNode()) { - if exportEquals := node.Symbol.Exports()[ast.InternalSymbolNameExportEquals]; exportEquals != nil && len(exportEquals.Declarations()) > 1 { + if exportEquals := node.Symbol.Exports().Get(ast.InternalSymbolNameExportEquals); exportEquals != nil && len(exportEquals.Declarations()) > 1 { for _, node := range exportEquals.Declarations() { tx.state.addDiagnostic(createDiagnosticForNode(node, diagnostics.Multiple_module_exports_assignments_cannot_be_serialized_for_declaration_emit)) } @@ -543,26 +543,39 @@ func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReferen return result } -func (tx *DeclarationTransformer) setupDiagnosticContext(input *ast.Node) (bool, func()) { +type savedDiagnosticContext struct { + oldDiag GetSymbolAccessibilityDiagnostic + oldDiagNode *ast.Node + oldDiagForName bool + oldName *ast.Node + oldWithinObjectLiteralType bool +} + +func (tx *DeclarationTransformer) setupDiagnosticContext(input *ast.Node) (bool, savedDiagnosticContext) { canProduceDiagnostic := canProduceDiagnostics(input) - oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts + saved := savedDiagnosticContext{ + oldDiag: tx.state.getSymbolAccessibilityDiagnostic, + oldDiagNode: tx.state.diagnosticNode, + oldDiagForName: tx.state.diagnosticForNodeName, + oldName: tx.state.errorNameNode, + oldWithinObjectLiteralType: tx.suppressNewDiagnosticContexts, + } shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && !(input.Parent.Kind == ast.KindTypeAliasDeclaration || input.Parent.Kind == ast.KindJSTypeAliasDeclaration) - - oldDiag := tx.state.getSymbolAccessibilityDiagnostic if canProduceDiagnostic && !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + tx.state.setDiagnosticNode(input, false) } - oldName := tx.state.errorNameNode - if shouldEnterSuppressNewDiagnosticsContextContext { tx.suppressNewDiagnosticContexts = true } + return canProduceDiagnostic, saved +} - return canProduceDiagnostic, func() { - tx.state.getSymbolAccessibilityDiagnostic = oldDiag - tx.state.errorNameNode = oldName - tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType - } +func (tx *DeclarationTransformer) restoreDiagnosticContext(saved savedDiagnosticContext) { + tx.state.getSymbolAccessibilityDiagnostic = saved.oldDiag + tx.state.diagnosticNode = saved.oldDiagNode + tx.state.diagnosticForNodeName = saved.oldDiagForName + tx.state.errorNameNode = saved.oldName + tx.suppressNewDiagnosticContexts = saved.oldWithinObjectLiteralType } func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node { @@ -611,9 +624,19 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. tx.enclosingDeclaration = input } - canProduceDiagnostic, cleanupDiagnosticContext := tx.setupDiagnosticContext(input) - defer cleanupDiagnosticContext() - + canProduceDiagnostic := canProduceDiagnostics(input) + oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts + shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && !(input.Parent.Kind == ast.KindTypeAliasDeclaration || input.Parent.Kind == ast.KindJSTypeAliasDeclaration) + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldDiagNode := tx.state.diagnosticNode + oldDiagForName := tx.state.diagnosticForNodeName + if canProduceDiagnostic && !tx.suppressNewDiagnosticContexts { + tx.state.setDiagnosticNode(input, false) + } + oldName := tx.state.errorNameNode + if shouldEnterSuppressNewDiagnosticsContextContext { + tx.suppressNewDiagnosticContexts = true + } var result *ast.Node switch input.Kind { @@ -697,13 +720,20 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. } tx.enclosingDeclaration = previousEnclosingDeclaration + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.diagnosticNode = oldDiagNode + tx.state.diagnosticForNodeName = oldDiagForName + tx.state.errorNameNode = oldName + tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType return result } func (tx *DeclarationTransformer) checkName(node *ast.Node) { oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldDiagNode := tx.state.diagnosticNode + oldDiagForName := tx.state.diagnosticForNodeName if !tx.suppressNewDiagnosticContexts { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNodeName(node) + tx.state.setDiagnosticNode(node, true) } tx.state.errorNameNode = node.Name() debug.Assert(ast.HasDynamicName(node)) // Should only be called with dynamic names @@ -711,6 +741,8 @@ func (tx *DeclarationTransformer) checkName(node *ast.Node) { tx.checkEntityNameVisibility(entityName, tx.enclosingDeclaration) if !tx.suppressNewDiagnosticContexts { tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.diagnosticNode = oldDiagNode + tx.state.diagnosticForNodeName = oldDiagForName } tx.state.errorNameNode = nil } @@ -1684,10 +1716,14 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) oldErrorNameNode := tx.state.errorNameNode tx.state.errorNameNode = node.Name() var oldDiag GetSymbolAccessibilityDiagnostic + var oldDiagNode *ast.Node + var oldDiagForName bool if !tx.suppressNewDiagnosticContexts { oldDiag = tx.state.getSymbolAccessibilityDiagnostic + oldDiagNode = tx.state.diagnosticNode + oldDiagForName = tx.state.diagnosticForNodeName if canProduceDiagnostics(node) { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) + tx.state.setDiagnosticNode(node, false) } } var typeNode *ast.Node @@ -1707,6 +1743,8 @@ func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) tx.state.errorNameNode = oldErrorNameNode if !tx.suppressNewDiagnosticContexts { tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.diagnosticNode = oldDiagNode + tx.state.diagnosticForNodeName = oldDiagForName } if typeNode == nil { return tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) @@ -1738,9 +1776,12 @@ func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) if input.Kind == ast.KindImportDeclaration || input.Kind == ast.KindJSImportDeclaration { res := tx.transformImportDeclaration(input.AsImportDeclaration()) if res != nil && res.Kind != ast.KindImportDeclaration { - res := res.Clone(tx.Factory()) - res.Kind = ast.KindImportDeclaration - return res + decl := res.AsImportDeclaration() + replacement := tx.Factory().NewImportDeclaration(decl.Modifiers(), decl.ImportClause, decl.ModuleSpecifier, decl.Attributes) + replacement.Flags = res.Flags + replacement.Loc = res.Loc + tx.EmitContext().SetOriginal(replacement, res) + return replacement } return res } @@ -1770,9 +1811,11 @@ func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) canProduceDiagnostic := canProduceDiagnostics(input) oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldDiagNode := tx.state.diagnosticNode + oldDiagForName := tx.state.diagnosticForNodeName oldName := tx.state.errorNameNode if canProduceDiagnostic { - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + tx.state.setDiagnosticNode(input, false) } saveNeedsDeclare := tx.needsDeclare @@ -1799,6 +1842,8 @@ func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) tx.enclosingDeclaration = previousEnclosingDeclaration tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.diagnosticNode = oldDiagNode + tx.state.diagnosticForNodeName = oldDiagForName tx.needsDeclare = saveNeedsDeclare tx.state.errorNameNode = oldName return result @@ -1953,11 +1998,13 @@ func (tx *DeclarationTransformer) buildClassMembers(classNode *ast.Node, extraMe var parameterProperties []*ast.Node if ctor != nil { oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldDiagNode := tx.state.diagnosticNode + oldDiagForName := tx.state.diagnosticForNodeName for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes { if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) || tx.shouldStripInternal(param) { continue } - tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param) + tx.state.setDiagnosticNode(param, false) if param.Name().Kind == ast.KindIdentifier { updated := tx.Factory().NewPropertyDeclaration( tx.ensureModifiers(param), @@ -1974,6 +2021,8 @@ func (tx *DeclarationTransformer) buildClassMembers(classNode *ast.Node, extraMe } } tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.diagnosticNode = oldDiagNode + tx.state.diagnosticForNodeName = oldDiagForName } // When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior @@ -2703,8 +2752,7 @@ func (tx *DeclarationTransformer) stripDeclareModifiers(node *ast.Node) *ast.Nod func (tx *DeclarationTransformer) visitCJSExportAssignments(expression *ast.Node) *ast.Node { if expression != nil { - _, cleanupDiagnosticContext := tx.setupDiagnosticContext(expression) - defer cleanupDiagnosticContext() + _, savedDiag := tx.setupDiagnosticContext(expression) switch ast.GetAssignmentDeclarationKind(expression) { case ast.JSDeclarationKindModuleExports: if tx.state.currentSourceFile.CommonJSModuleIndicator != nil { @@ -2716,15 +2764,16 @@ func (tx *DeclarationTransformer) visitCJSExportAssignments(expression *ast.Node } } } - return tx.cjsExportAssignmentVisitor.VisitEachChild(expression) // recur through the whole tree, looking for module.exports= + res := tx.cjsExportAssignmentVisitor.VisitEachChild(expression) // recur through the whole tree, looking for module.exports= + tx.restoreDiagnosticContext(savedDiag) + return res } return nil } func (tx *DeclarationTransformer) visitNestedExpression(expression *ast.Node) *ast.Node { if expression != nil { - _, cleanupDiagnosticContext := tx.setupDiagnosticContext(expression) - defer cleanupDiagnosticContext() + _, savedDiag := tx.setupDiagnosticContext(expression) switch ast.GetAssignmentDeclarationKind(expression) { case ast.JSDeclarationKindProperty: tx.transformExpandoAssignment(expression.AsBinaryExpression()) @@ -2743,7 +2792,9 @@ func (tx *DeclarationTransformer) visitNestedExpression(expression *ast.Node) *a } } } - return tx.expressionVisitor.VisitEachChild(expression) // recur through the whole tree, looking for special assignments + res := tx.expressionVisitor.VisitEachChild(expression) // recur through the whole tree, looking for special assignments + tx.restoreDiagnosticContext(savedDiag) + return res } return nil } @@ -2820,8 +2871,7 @@ func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExp localName = tx.Factory().NewGeneratedNameForNode(node.AsNode()) } - _, cleanupDiagnosticContext := tx.setupDiagnosticContext(node.AsNode()) - defer cleanupDiagnosticContext() + _, savedDiag := tx.setupDiagnosticContext(node.AsNode()) preexistingExpandoHasExport := core.Some(tx.expandoMembers[hostId], ast.IsExportDeclaration) @@ -2832,6 +2882,7 @@ func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExp // alias-like, emit an `export {name}` or `export {name as alias}` result := tx.transformBinaryExpressionToExportDeclaration(node.AsNode(), exportName) tx.expandoMembers[hostId] = append(tx.expandoMembers[hostId], result) + tx.restoreDiagnosticContext(savedDiag) return } @@ -2846,14 +2897,11 @@ func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExp declarationData := synthesizedNamespace.DeclarationData() declarationData.Symbol = host containerData := synthesizedNamespace.LocalsContainerData() - containerData.Locals = make(ast.SymbolTable, 0) - containerData.Locals[localName.Text()] = symbol + containerData.Locals = ast.NewSymbolTable() + containerData.Locals.Set(localName.Text(), symbol) oldEnclosing := tx.enclosingDeclaration tx.enclosingDeclaration = synthesizedNamespace - defer func() { - tx.enclosingDeclaration = oldEnclosing - }() statements := []*ast.Statement{ tx.Factory().NewVariableStatement( @@ -2880,7 +2928,9 @@ func (tx *DeclarationTransformer) transformExpandoAssignment(node *ast.BinaryExp } } + tx.enclosingDeclaration = oldEnclosing tx.expandoMembers[hostId] = append(tx.expandoMembers[hostId], statements...) + tx.restoreDiagnosticContext(savedDiag) } func (tx *DeclarationTransformer) addExportModifierToExpandoMembers(hostId ast.NodeId) { @@ -2920,8 +2970,7 @@ func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declarati modifierFlags ^= ast.ModifierFlagsExport } - _, cleanupDiagnosticContext := tx.setupDiagnosticContext(declaration) - defer cleanupDiagnosticContext() + _, savedDiag := tx.setupDiagnosticContext(declaration) modifiers := tx.Factory().NewModifierList(ast.CreateModifiersFromModifierFlags(modifierFlags, tx.Factory().NewModifier)) replacement := make([]*ast.Node, 0) @@ -2935,6 +2984,7 @@ func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declarati replacement = append(replacement, tx.Factory().NewFunctionDeclaration(modifiers, asteriskToken, tx.Factory().NewIdentifier(name.Text()), tx.ensureTypeParams(fn, typeParameters), tx.updateParamList(fn, parameters), tx.ensureType(fn, false), nil /*fullSignature*/, nil /*body*/)) } else { tx.expandoHosts[id] = tx.transformTopLevelDeclaration(declaration) + tx.restoreDiagnosticContext(savedDiag) return } @@ -2953,6 +3003,7 @@ func (tx *DeclarationTransformer) transformExpandoHost(name *ast.Node, declarati if _, ok := tx.lateStatementReplacementMap[id]; ok { tx.lateStatementReplacementMap[id] = tx.createFullExpandoBlock(id) } + tx.restoreDiagnosticContext(savedDiag) } func (tx *DeclarationTransformer) createFullExpandoBlock(id ast.NodeId) *ast.Node { diff --git a/tsc/internal/transformers/tstransforms/runtimesyntax.go b/tsc/internal/transformers/tstransforms/runtimesyntax.go index 360a39e323b86..fa413d1f59a03 100644 --- a/tsc/internal/transformers/tstransforms/runtimesyntax.go +++ b/tsc/internal/transformers/tstransforms/runtimesyntax.go @@ -557,6 +557,11 @@ func (tx *RuntimeSyntaxTransformer) visitImportEqualsDeclaration(node *ast.Impor } moduleReference := tx.Factory().CreateExpressionFromEntityName(node.ModuleReference) + if ast.IsIdentifier(moduleReference) { + moduleReference = tx.visitExpressionIdentifier(moduleReference) + } else { + moduleReference = tx.Visitor().VisitNode(moduleReference) + } tx.EmitContext().SetEmitFlags(moduleReference, printer.EFNoComments|printer.EFNoNestedComments) if !tx.isExportOfNamespace(node.AsNode()) { // export var ${name} = ${moduleReference}; diff --git a/tsc/internal/tsoptions/declarations_generated.go b/tsc/internal/tsoptions/declarations_generated.go index 7f3b37706c4b9..097384b5fd5cf 100644 --- a/tsc/internal/tsoptions/declarations_generated.go +++ b/tsc/internal/tsoptions/declarations_generated.go @@ -234,6 +234,13 @@ var commonOptionsWithBuild = []*CommandLineOption{ DefaultValueDescription: diagnostics.X_4_unless_singleThreaded_is_passed, minValue: 1, }, + { + Name: "maxMemory", + Kind: CommandLineOptionTypeNumber, + Category: diagnostics.Command_line_Options, + Description: diagnostics.Set_the_memory_limit_in_MiB_for_recycling_checkers_during_no_emit_type_checking, + minValue: 1, + }, { Name: "runExternalCode", Kind: CommandLineOptionTypeBoolean, diff --git a/tsc/internal/tsoptions/options_generated.go b/tsc/internal/tsoptions/options_generated.go index 975913279a80c..f39267dc03913 100644 --- a/tsc/internal/tsoptions/options_generated.go +++ b/tsc/internal/tsoptions/options_generated.go @@ -288,6 +288,8 @@ func parseCompilerOptions(key string, value any, allOptions *core.CompilerOption allOptions.Quiet = ParseTristate(value) case "checkers": allOptions.Checkers = parseNumber(value) + case "maxMemory": + allOptions.MaxMemory = parseNumber(value) default: return false } @@ -1327,6 +1329,11 @@ func mergeCompilerOptionFields(targetOptions, sourceOptions *core.CompilerOption } else if sourceOptions.Checkers != nil { targetOptions.Checkers = sourceOptions.Checkers } + if explicitNullFields.Has("maxMemory") { + targetOptions.MaxMemory = nil + } else if sourceOptions.MaxMemory != nil { + targetOptions.MaxMemory = sourceOptions.MaxMemory + } } func handleOptionConfigDirTemplateSubstitution(compilerOptions *parsedCompilerOptions, basePath tspath.RootedDirectoryPath) { diff --git a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.errors.txt b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.errors.txt new file mode 100644 index 0000000000000..cfda8b47dd1ab --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.errors.txt @@ -0,0 +1,24 @@ +file2.ts(4,16): error TS2671: Cannot augment module './file1' because it resolves to a non-module entity. +file3.ts(3,8): error TS2503: Cannot find namespace 'x'. + + +==== file3.ts (1 errors) ==== + import x = require("./file1"); + import "./file2"; + let a: x.A; // should not work + ~ +!!! error TS2503: Cannot find namespace 'x'. +==== file1.ts (0 errors) ==== + function foo() {} + export = foo; + +==== file2.ts (1 errors) ==== + import x = require("./file1"); + + // should error since './file1' does not have namespace meaning + declare module "./file1" { + ~~~~~~~~~ +!!! error TS2671: Cannot augment module './file1' because it resolves to a non-module entity. + interface A { a } + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.js b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.js index cab4b9fb56ff9..30c796d8365ae 100644 --- a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.js +++ b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.js @@ -12,12 +12,20 @@ declare module "./file1" { interface A { a } } -//// [file3.ts] - //// [file3.ts] import x = require("./file1"); import "./file2"; let a: x.A; // should not work +//// [file1.js] +"use strict"; +function foo() { } +module.exports = foo; +//// [file2.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); //// [file3.js] "use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +require("./file2"); +let a; // should not work diff --git a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.symbols b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.symbols index 1f758cf8e600d..3a47af235c0bc 100644 --- a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.symbols +++ b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.symbols @@ -1,9 +1,32 @@ //// [tests/cases/compiler/augmentExportEquals2.ts] //// === file3.ts === - import x = require("./file1"); +>x : Symbol(x, Decl(file3.ts, 0, 0)) + import "./file2"; let a: x.A; // should not work -=== file3.ts === +>a : Symbol(a, Decl(file3.ts, 2, 3)) +>x : Symbol(x) +>A : Symbol(x.A) + +=== file1.ts === +function foo() {} +>foo : Symbol(foo, Decl(file1.ts, 0, 0)) + +export = foo; +>foo : Symbol(foo, Decl(file1.ts, 0, 0)) + +=== file2.ts === +import x = require("./file1"); +>x : Symbol(x, Decl(file2.ts, 0, 0)) + +// should error since './file1' does not have namespace meaning +declare module "./file1" { +>"./file1" : Symbol("./file1", Decl(file2.ts, 0, 30)) + + interface A { a } +>A : Symbol(A, Decl(file2.ts, 3, 26)) +>a : Symbol(A.a, Decl(file2.ts, 4, 17)) +} diff --git a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.types b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.types index 1f758cf8e600d..a428d630e6c7a 100644 --- a/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.types +++ b/tsc/testdata/baselines/reference/compiler/augmentExportEquals2.types @@ -1,9 +1,30 @@ //// [tests/cases/compiler/augmentExportEquals2.ts] //// === file3.ts === - import x = require("./file1"); +>x : () => void + import "./file2"; let a: x.A; // should not work -=== file3.ts === +>a : x.A +>x : any + +=== file1.ts === +function foo() {} +>foo : () => void + +export = foo; +>foo : () => void + +=== file2.ts === +import x = require("./file1"); +>x : () => void + +// should error since './file1' does not have namespace meaning +declare module "./file1" { +>"./file1" : any + + interface A { a } +>a : any +} diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js new file mode 100644 index 0000000000000..935e9baeed1ad --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +//// [index.d.ts] +export { v as b } from "ia"; +export { v as a } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; + +//// [gadget.d.ts] +export interface Gadget { id: number } + +//// [widget.d.ts] +import { Gadget } from "./gadget"; +export declare function makeWidget(): Gadget; + +//// [main.ts] +/// +import { f1 } from "./index"; +import { makeWidget } from "./widget"; +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const w = makeWidget(); +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const keep: 1; +export declare const w: import("./gadget").Gadget; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.symbols b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.symbols new file mode 100644 index 0000000000000..3847b78195e27 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.symbols @@ -0,0 +1,180 @@ +//// [tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "./index"; +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +import { makeWidget } from "./widget"; +>makeWidget : Symbol(makeWidget, Decl(main.ts, 2, 8)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 2, 38)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) + +export const keep = f1; +>keep : Symbol(keep, Decl(main.ts, 4, 12)) +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +export const w = makeWidget(); +>w : Symbol(w, Decl(main.ts, 5, 12)) +>makeWidget : Symbol(makeWidget, Decl(main.ts, 2, 8)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 38)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 7, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 38)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + +=== /index.d.ts === +export { v as b } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>b : Symbol(b, Decl(index.d.ts, 0, 8)) + +export { v as a } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>a : Symbol(a, Decl(index.d.ts, 1, 8)) + +export declare const f1: 1; +>f1 : Symbol(f1, Decl(index.d.ts, 2, 20)) + +export declare const f2: 2; +>f2 : Symbol(f2, Decl(index.d.ts, 3, 20)) + +export declare const f3: 3; +>f3 : Symbol(f3, Decl(index.d.ts, 4, 20)) + +export declare const f4: 4; +>f4 : Symbol(f4, Decl(index.d.ts, 5, 20)) + +export declare const f5: 5; +>f5 : Symbol(f5, Decl(index.d.ts, 6, 20)) + +export declare const f6: 6; +>f6 : Symbol(f6, Decl(index.d.ts, 7, 20)) + +export declare const f7: 7; +>f7 : Symbol(f7, Decl(index.d.ts, 8, 20)) + +export declare const f8: 8; +>f8 : Symbol(f8, Decl(index.d.ts, 9, 20)) + +export declare const f9: 9; +>f9 : Symbol(f9, Decl(index.d.ts, 10, 20)) + +export declare const f10: 10; +>f10 : Symbol(f10, Decl(index.d.ts, 11, 20)) + +=== /gadget.d.ts === +export interface Gadget { id: number } +>Gadget : Symbol(Gadget, Decl(gadget.d.ts, 0, 0)) +>id : Symbol(Gadget.id, Decl(gadget.d.ts, 0, 25)) + +=== /widget.d.ts === +import { Gadget } from "./gadget"; +>Gadget : Symbol(Gadget, Decl(widget.d.ts, 0, 8)) + +export declare function makeWidget(): Gadget; +>makeWidget : Symbol(makeWidget, Decl(widget.d.ts, 0, 34)) +>Gadget : Symbol(Gadget, Decl(widget.d.ts, 0, 8)) + diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types new file mode 100644 index 0000000000000..f7cb243d2a23e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types @@ -0,0 +1,164 @@ +//// [tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "./index"; +>f1 : 1 + +import { makeWidget } from "./widget"; +>makeWidget : () => import("./gadget").Gadget + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const keep = f1; +>keep : 1 +>f1 : 1 + +export const w = makeWidget(); +>w : import("./gadget").Gadget +>makeWidget() : import("./gadget").Gadget +>makeWidget : () => import("./gadget").Gadget + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + +=== /index.d.ts === +export { v as b } from "ia"; +>v : 0 +>b : 0 + +export { v as a } from "ib"; +>v : 0 +>a : 0 + +export declare const f1: 1; +>f1 : 1 + +export declare const f2: 2; +>f2 : 2 + +export declare const f3: 3; +>f3 : 3 + +export declare const f4: 4; +>f4 : 4 + +export declare const f5: 5; +>f5 : 5 + +export declare const f6: 6; +>f6 : 6 + +export declare const f7: 7; +>f7 : 7 + +export declare const f8: 8; +>f8 : 8 + +export declare const f9: 9; +>f9 : 9 + +export declare const f10: 10; +>f10 : 10 + +=== /gadget.d.ts === +export interface Gadget { id: number } +>id : number + +=== /widget.d.ts === +import { Gadget } from "./gadget"; +>Gadget : any + +export declare function makeWidget(): Gadget; +>makeWidget : () => Gadget + diff --git a/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.errors.txt b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.errors.txt new file mode 100644 index 0000000000000..c429753fd0a39 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.errors.txt @@ -0,0 +1,24 @@ +/d.ts(1,10): error TS2305: Module '"./c"' has no exported member 'I'. + + +==== /a.ts (0 errors) ==== + interface I {} + export default I; + export interface J {} + +==== /b.ts (0 errors) ==== + export {}; + declare module './a' { + export default interface I { x: number; } + } + +==== /c.ts (0 errors) ==== + export * from "./a"; + +==== /d.ts (1 errors) ==== + import { I, J } from "./c"; + ~ +!!! error TS2305: Module '"./c"' has no exported member 'I'. + export const i: I = { x: 0 }; + export const j: J = {}; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.js b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.js new file mode 100644 index 0000000000000..8b8f7e4235860 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts] //// + +//// [a.ts] +interface I {} +export default I; +export interface J {} + +//// [b.ts] +export {}; +declare module './a' { + export default interface I { x: number; } +} + +//// [c.ts] +export * from "./a"; + +//// [d.ts] +import { I, J } from "./c"; +export const i: I = { x: 0 }; +export const j: J = {}; + + +//// [a.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [b.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [c.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./a"), exports); +//// [d.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.j = exports.i = void 0; +exports.i = { x: 0 }; +exports.j = {}; + + +//// [a.d.ts] +interface I { +} +export default I; +export interface J { +} +//// [b.d.ts] +export {}; +declare module './a' { + export default interface I { + x: number; + } +} +//// [c.d.ts] +export * from "./a"; +//// [d.d.ts] +import { I, J } from "./c"; +export declare const i: I; +export declare const j: J; diff --git a/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.symbols b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.symbols new file mode 100644 index 0000000000000..b1c1a85c0b06f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.symbols @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts] //// + +=== /a.ts === +interface I {} +>I : Symbol(I, Decl(a.ts, 0, 0), Decl(b.ts, 1, 22)) + +export default I; +>I : Symbol(I, Decl(a.ts, 0, 0), Decl(b.ts, 1, 22)) + +export interface J {} +>J : Symbol(J, Decl(a.ts, 1, 17)) + +=== /b.ts === +export {}; +declare module './a' { +>'./a' : Symbol("./a", Decl(a.ts, 0, 0), Decl(b.ts, 0, 10)) + + export default interface I { x: number; } +>I : Symbol(I, Decl(a.ts, 0, 0), Decl(b.ts, 1, 22)) +>x : Symbol(I.x, Decl(b.ts, 2, 32)) +} + +=== /c.ts === + +export * from "./a"; + +=== /d.ts === +import { I, J } from "./c"; +>I : Symbol(I, Decl(d.ts, 0, 8)) +>J : Symbol(J, Decl(d.ts, 0, 11)) + +export const i: I = { x: 0 }; +>i : Symbol(i, Decl(d.ts, 1, 12)) +>I : Symbol(I, Decl(d.ts, 0, 8)) +>x : Symbol(x, Decl(d.ts, 1, 21)) + +export const j: J = {}; +>j : Symbol(j, Decl(d.ts, 2, 12)) +>J : Symbol(J, Decl(d.ts, 0, 11)) + diff --git a/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.types b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.types new file mode 100644 index 0000000000000..b9a61d4eb5ac0 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/exportStarSkipsNamedDefaultExport.types @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts] //// + +=== /a.ts === +interface I {} +export default I; +>I : I + +export interface J {} + +=== /b.ts === +export {}; +declare module './a' { +>'./a' : typeof import("./a") + + export default interface I { x: number; } +>x : number +} + +=== /c.ts === + +export * from "./a"; + +=== /d.ts === +import { I, J } from "./c"; +>I : any +>J : any + +export const i: I = { x: 0 }; +>i : I +>{ x: 0 } : { x: number; } +>x : number +>0 : 0 + +export const j: J = {}; +>j : J +>{} : {} + diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt new file mode 100644 index 0000000000000..f9e0d6ab4eac5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt @@ -0,0 +1,64 @@ +/main.ts(7,32): error TS2339: Property 'mb' does not exist on type '{}'. + + +==== /main.ts (1 errors) ==== + import * as idx from "./index"; + const all = { ...idx }; + import * as MA from "./ma"; + import * as MB from "./mb"; + export const fromA = MA.pa; + export const fromB = MB.pb; + export const checkA: 1 = MA.pa.mb; + ~~ +!!! error TS2339: Property 'mb' does not exist on type '{}'. + export const checkB: 1 = MB.pb.ma; + +==== /ma.js (0 errors) ==== + export { v as pa } from "./xa"; + export const ma = 1; + +==== /mb.js (0 errors) ==== + export { v as pb } from "./xb"; + export const mb = 1; + +==== /xa.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /xb.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ia.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ib.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /star.js (0 errors) ==== + export { v as a } from "./ia"; + +==== /index.js (0 errors) ==== + export { v as b } from "./ib"; + export * from "./star"; + export const f1 = 1; + export const f2 = 2; + export const f3 = 3; + export const f4 = 4; + export const f5 = 5; + export const f6 = 6; + export const f7 = 7; + export const f8 = 8; + export const f9 = 9; + export const f10 = 10; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js new file mode 100644 index 0000000000000..b99218d5bfd65 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js @@ -0,0 +1,245 @@ +//// [tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts] //// + +//// [ma.js] +export { v as pa } from "./xa"; +export const ma = 1; + +//// [mb.js] +export { v as pb } from "./xb"; +export const mb = 1; + +//// [xa.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [xb.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ia.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ib.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [star.js] +export { v as a } from "./ia"; + +//// [index.js] +export { v as b } from "./ib"; +export * from "./star"; +export const f1 = 1; +export const f2 = 2; +export const f3 = 3; +export const f4 = 4; +export const f5 = 5; +export const f6 = 6; +export const f7 = 7; +export const f8 = 8; +export const f9 = 9; +export const f10 = 10; + +//// [main.ts] +import * as idx from "./index"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; + + +//// [xa.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ma.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ma = exports.pa = void 0; +var xa_1 = require("./xa"); +Object.defineProperty(exports, "pa", { enumerable: true, get: function () { return xa_1.v; } }); +exports.ma = 1; +//// [xb.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [mb.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mb = exports.pb = void 0; +var xb_1 = require("./xb"); +Object.defineProperty(exports, "pb", { enumerable: true, get: function () { return xb_1.v; } }); +exports.mb = 1; +//// [ib.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ia.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [star.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.a = void 0; +var ia_1 = require("./ia"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return ia_1.v; } }); +//// [index.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.f10 = exports.f9 = exports.f8 = exports.f7 = exports.f6 = exports.f5 = exports.f4 = exports.f3 = exports.f2 = exports.f1 = exports.b = void 0; +var ib_1 = require("./ib"); +Object.defineProperty(exports, "b", { enumerable: true, get: function () { return ib_1.v; } }); +__exportStar(require("./star"), exports); +exports.f1 = 1; +exports.f2 = 2; +exports.f3 = 3; +exports.f4 = 4; +exports.f5 = 5; +exports.f6 = 6; +exports.f7 = 7; +exports.f8 = 8; +exports.f9 = 9; +exports.f10 = 10; +//// [main.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkB = exports.checkA = exports.fromB = exports.fromA = void 0; +const idx = __importStar(require("./index")); +const all = { ...idx }; +const MA = __importStar(require("./ma")); +const MB = __importStar(require("./mb")); +exports.fromA = MA.pa; +exports.fromB = MB.pb; +exports.checkA = MA.pa.mb; +exports.checkB = MB.pb.ma; + + +//// [xa.d.ts] +export = o; +declare const o: { + v: {}; +}; +//// [ma.d.ts] +export { v as pa } from "./xa"; +export declare const ma = 1; +//// [xb.d.ts] +export = o; +declare const o: { + v: { + pa: {}; + ma: 1; + }; +}; +//// [mb.d.ts] +export { v as pb } from "./xb"; +export declare const mb = 1; +//// [ib.d.ts] +export = o; +declare const o: { + v: { + pb: { + pa: {}; + ma: 1; + }; + mb: 1; + }; +}; +//// [ia.d.ts] +export = o; +declare const o: { + v: { + pa: {}; + ma: 1; + }; +}; +//// [star.d.ts] +export { v as a } from "./ia"; +//// [index.d.ts] +export { v as b } from "./ib"; +export * from "./star"; +export declare const f1 = 1; +export declare const f2 = 2; +export declare const f3 = 3; +export declare const f4 = 4; +export declare const f5 = 5; +export declare const f6 = 6; +export declare const f7 = 7; +export declare const f8 = 8; +export declare const f9 = 9; +export declare const f10 = 10; +//// [main.d.ts] +export declare const fromA: {}; +export declare const fromB: { + pa: {}; + ma: 1; +}; +export declare const checkA: 1; +export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols new file mode 100644 index 0000000000000..4f7c5a9b9f1d4 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols @@ -0,0 +1,163 @@ +//// [tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts] //// + +=== /main.ts === +import * as idx from "./index"; +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +const all = { ...idx }; +>all : Symbol(all, Decl(main.ts, 1, 5)) +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +import * as MA from "./ma"; +>MA : Symbol(MA, Decl(main.ts, 2, 6)) + +import * as MB from "./mb"; +>MB : Symbol(MB, Decl(main.ts, 3, 6)) + +export const fromA = MA.pa; +>fromA : Symbol(fromA, Decl(main.ts, 4, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) + +export const fromB = MB.pb; +>fromB : Symbol(fromB, Decl(main.ts, 5, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) + +export const checkA: 1 = MA.pa.mb; +>checkA : Symbol(checkA, Decl(main.ts, 6, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) + +export const checkB: 1 = MB.pb.ma; +>checkB : Symbol(checkB, Decl(main.ts, 7, 12)) +>MB.pb.ma : Symbol(ma, Decl(ma.js, 1, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>ma : Symbol(ma, Decl(ma.js, 1, 12)) + +=== /ma.js === +export { v as pa } from "./xa"; +>v : Symbol(v, Decl(xa.js, 2, 11)) +>pa : Symbol(pa, Decl(ma.js, 0, 8)) + +export const ma = 1; +>ma : Symbol(ma, Decl(ma.js, 1, 12)) + +=== /mb.js === +export { v as pb } from "./xb"; +>v : Symbol(v, Decl(xb.js, 2, 11)) +>pb : Symbol(pb, Decl(mb.js, 0, 8)) + +export const mb = 1; +>mb : Symbol(mb, Decl(mb.js, 1, 12)) + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xa.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xa.js, 2, 5)) +>v : Symbol(v, Decl(xa.js, 2, 11)) +>M : Symbol(M, Decl(xa.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xa.js, 0, 0)) +>module : Symbol(module, Decl(xa.js, 0, 0)) +>exports : Symbol(exports, Decl(xa.js, 0, 0)) +>o : Symbol(o, Decl(xa.js, 2, 5)) + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xb.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xb.js, 2, 5)) +>v : Symbol(v, Decl(xb.js, 2, 11)) +>M : Symbol(M, Decl(xb.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xb.js, 0, 0)) +>module : Symbol(module, Decl(xb.js, 0, 0)) +>exports : Symbol(exports, Decl(xb.js, 0, 0)) +>o : Symbol(o, Decl(xb.js, 2, 5)) + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ia.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ia.js, 2, 5)) +>v : Symbol(v, Decl(ia.js, 2, 11)) +>M : Symbol(M, Decl(ia.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ia.js, 0, 0)) +>module : Symbol(module, Decl(ia.js, 0, 0)) +>exports : Symbol(exports, Decl(ia.js, 0, 0)) +>o : Symbol(o, Decl(ia.js, 2, 5)) + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ib.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ib.js, 2, 5)) +>v : Symbol(v, Decl(ib.js, 2, 11)) +>M : Symbol(M, Decl(ib.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ib.js, 0, 0)) +>module : Symbol(module, Decl(ib.js, 0, 0)) +>exports : Symbol(exports, Decl(ib.js, 0, 0)) +>o : Symbol(o, Decl(ib.js, 2, 5)) + +=== /star.js === +export { v as a } from "./ia"; +>v : Symbol(v, Decl(ia.js, 2, 11)) +>a : Symbol(a, Decl(star.js, 0, 8)) + +=== /index.js === +export { v as b } from "./ib"; +>v : Symbol(v, Decl(ib.js, 2, 11)) +>b : Symbol(b, Decl(index.js, 0, 8)) + +export * from "./star"; +export const f1 = 1; +>f1 : Symbol(f1, Decl(index.js, 2, 12)) + +export const f2 = 2; +>f2 : Symbol(f2, Decl(index.js, 3, 12)) + +export const f3 = 3; +>f3 : Symbol(f3, Decl(index.js, 4, 12)) + +export const f4 = 4; +>f4 : Symbol(f4, Decl(index.js, 5, 12)) + +export const f5 = 5; +>f5 : Symbol(f5, Decl(index.js, 6, 12)) + +export const f6 = 6; +>f6 : Symbol(f6, Decl(index.js, 7, 12)) + +export const f7 = 7; +>f7 : Symbol(f7, Decl(index.js, 8, 12)) + +export const f8 = 8; +>f8 : Symbol(f8, Decl(index.js, 9, 12)) + +export const f9 = 9; +>f9 : Symbol(f9, Decl(index.js, 10, 12)) + +export const f10 = 10; +>f10 : Symbol(f10, Decl(index.js, 11, 12)) + diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types new file mode 100644 index 0000000000000..179ce8b0ee07e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types @@ -0,0 +1,194 @@ +//// [tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts] //// + +=== /main.ts === +import * as idx from "./index"; +>idx : typeof idx + +const all = { ...idx }; +>all : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>idx : typeof idx + +import * as MA from "./ma"; +>MA : typeof MA + +import * as MB from "./mb"; +>MB : typeof MB + +export const fromA = MA.pa; +>fromA : {} +>MA.pa : {} +>MA : typeof MA +>pa : {} + +export const fromB = MB.pb; +>fromB : { pa: {}; ma: 1; } +>MB.pb : { pa: {}; ma: 1; } +>MB : typeof MB +>pb : { pa: {}; ma: 1; } + +export const checkA: 1 = MA.pa.mb; +>checkA : 1 +>MA.pa.mb : any +>MA.pa : {} +>MA : typeof MA +>pa : {} +>mb : any + +export const checkB: 1 = MB.pb.ma; +>checkB : 1 +>MB.pb.ma : 1 +>MB.pb : { pa: {}; ma: 1; } +>MB : typeof MB +>pb : { pa: {}; ma: 1; } +>ma : 1 + +=== /ma.js === +export { v as pa } from "./xa"; +>v : {} +>pa : {} + +export const ma = 1; +>ma : 1 +>1 : 1 + +=== /mb.js === +export { v as pb } from "./xb"; +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } + +export const mb = 1; +>mb : 1 +>1 : 1 + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module : { exports: { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; }; } +>exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } + +=== /star.js === +export { v as a } from "./ia"; +>v : { pa: {}; ma: 1; } +>a : { pa: {}; ma: 1; } + +=== /index.js === +export { v as b } from "./ib"; +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>b : { pb: { pa: {}; ma: 1; }; mb: 1; } + +export * from "./star"; +export const f1 = 1; +>f1 : 1 +>1 : 1 + +export const f2 = 2; +>f2 : 2 +>2 : 2 + +export const f3 = 3; +>f3 : 3 +>3 : 3 + +export const f4 = 4; +>f4 : 4 +>4 : 4 + +export const f5 = 5; +>f5 : 5 +>5 : 5 + +export const f6 = 6; +>f6 : 6 +>6 : 6 + +export const f7 = 7; +>f7 : 7 +>7 : 7 + +export const f8 = 8; +>f8 : 8 +>8 : 8 + +export const f9 = 9; +>f9 : 9 +>9 : 9 + +export const f10 = 10; +>f10 : 10 +>10 : 10 + diff --git a/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.symbols b/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.symbols new file mode 100644 index 0000000000000..62ad300d4e6c5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.symbols @@ -0,0 +1,77 @@ +//// [tests/cases/compiler/narrowingNoInfer2.ts] //// + +=== narrowingNoInfer2.ts === +type U1 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; +>U1 : Symbol(U1, Decl(narrowingNoInfer2.ts, 0, 0)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 0, 19)) +>a : Symbol(a, Decl(narrowingNoInfer2.ts, 0, 30)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 0, 46)) +>b : Symbol(b, Decl(narrowingNoInfer2.ts, 0, 57)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 0, 74)) + +function f1(u: U1) { +>f1 : Symbol(f1, Decl(narrowingNoInfer2.ts, 0, 87)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 2, 12)) +>U1 : Symbol(U1, Decl(narrowingNoInfer2.ts, 0, 0)) + + if (u.kind === "a") { +>u.kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 0, 19), Decl(narrowingNoInfer2.ts, 0, 46), Decl(narrowingNoInfer2.ts, 0, 74)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 2, 12)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 0, 19), Decl(narrowingNoInfer2.ts, 0, 46), Decl(narrowingNoInfer2.ts, 0, 74)) + + u.a; +>u.a : Symbol(a, Decl(narrowingNoInfer2.ts, 0, 30)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 2, 12)) +>a : Symbol(a, Decl(narrowingNoInfer2.ts, 0, 30)) + } +} + +type U2 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; +>U2 : Symbol(U2, Decl(narrowingNoInfer2.ts, 6, 1)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 8, 19)) +>a : Symbol(a, Decl(narrowingNoInfer2.ts, 8, 30)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 8, 46)) +>b : Symbol(b, Decl(narrowingNoInfer2.ts, 8, 57)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 8, 74)) + +interface Box { +>Box : Symbol(Box, Decl(narrowingNoInfer2.ts, 8, 87)) +>T : Symbol(T, Decl(narrowingNoInfer2.ts, 10, 14)) + + u: U2; +>u : Symbol(Box.u, Decl(narrowingNoInfer2.ts, 10, 18)) +>U2 : Symbol(U2, Decl(narrowingNoInfer2.ts, 6, 1)) + + t: T; +>t : Symbol(Box.t, Decl(narrowingNoInfer2.ts, 11, 10)) +>T : Symbol(T, Decl(narrowingNoInfer2.ts, 10, 14)) +} + +declare const box: Box; +>box : Symbol(box, Decl(narrowingNoInfer2.ts, 15, 13)) +>Box : Symbol(Box, Decl(narrowingNoInfer2.ts, 8, 87)) + +box.u; +>box.u : Symbol(Box.u, Decl(narrowingNoInfer2.ts, 10, 18)) +>box : Symbol(box, Decl(narrowingNoInfer2.ts, 15, 13)) +>u : Symbol(Box.u, Decl(narrowingNoInfer2.ts, 10, 18)) + +function f2(u: U2) { +>f2 : Symbol(f2, Decl(narrowingNoInfer2.ts, 16, 6)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 18, 12)) +>U2 : Symbol(U2, Decl(narrowingNoInfer2.ts, 6, 1)) + + if (u.kind === "a") { +>u.kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 8, 19), Decl(narrowingNoInfer2.ts, 8, 46), Decl(narrowingNoInfer2.ts, 8, 74)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 18, 12)) +>kind : Symbol(kind, Decl(narrowingNoInfer2.ts, 8, 19), Decl(narrowingNoInfer2.ts, 8, 46), Decl(narrowingNoInfer2.ts, 8, 74)) + + u.a; +>u.a : Symbol(a, Decl(narrowingNoInfer2.ts, 8, 30)) +>u : Symbol(u, Decl(narrowingNoInfer2.ts, 18, 12)) +>a : Symbol(a, Decl(narrowingNoInfer2.ts, 8, 30)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.types b/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.types new file mode 100644 index 0000000000000..7c6857340b3fd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/narrowingNoInfer2.types @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/narrowingNoInfer2.ts] //// + +=== narrowingNoInfer2.ts === +type U1 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; +>U1 : U1 +>kind : "a" +>a : number +>kind : "b" +>b : number +>kind : "c" + +function f1(u: U1) { +>f1 : (u: U1) => void +>u : U1 + + if (u.kind === "a") { +>u.kind === "a" : boolean +>u.kind : "a" | "b" | "c" +>u : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; } +>kind : "a" | "b" | "c" +>"a" : "a" + + u.a; +>u.a : number +>u : { kind: "a"; a: number; } +>a : number + } +} + +type U2 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; +>U2 : U2 +>kind : "a" +>a : number +>kind : "b" +>b : number +>kind : "c" + +interface Box { + u: U2; +>u : U2 + + t: T; +>t : T +} + +declare const box: Box; +>box : Box + +box.u; +>box.u : U2 +>box : Box +>u : U2 + +function f2(u: U2) { +>f2 : (u: U2) => void +>u : U2 + + if (u.kind === "a") { +>u.kind === "a" : boolean +>u.kind : "a" | "b" | "c" +>u : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; } +>kind : "a" | "b" | "c" +>"a" : "a" + + u.a; +>u.a : number +>u : { kind: "a"; a: number; } +>a : number + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js new file mode 100644 index 0000000000000..2413425ee0027 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js @@ -0,0 +1,82 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mt" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; +} +declare module "mm" { + import t = require("mt"); + export = t; + export const C: unique symbol; + global { + const gc: { c: typeof C }; + } +} + +//// [main.ts] +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const w = gc; +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const w: { + c: typeof import("mm").C; +}; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.symbols new file mode 100644 index 0000000000000..43d526421d7bb --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.symbols @@ -0,0 +1,188 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts] //// + +=== /main.ts === +/// +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 1, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 2, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 2, 39)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) + +export const w = gc; +>w : Symbol(w, Decl(main.ts, 4, 12)) +>gc : Symbol(gc, Decl(ambient.d.ts, 51, 13)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 5, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 1, 13)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 2, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "mt" { +>"mt" : Symbol("mt", Decl(ambient.d.ts, 31, 1)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 33, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 34, 12)) + + export const f1: 1; +>f1 : Symbol(f1, Decl(ambient.d.ts, 35, 16)) + + export const f2: 2; +>f2 : Symbol(f2, Decl(ambient.d.ts, 36, 16)) + + export const f3: 3; +>f3 : Symbol(f3, Decl(ambient.d.ts, 37, 16)) + + export const f4: 4; +>f4 : Symbol(f4, Decl(ambient.d.ts, 38, 16)) + + export const f5: 5; +>f5 : Symbol(f5, Decl(ambient.d.ts, 39, 16)) + + export const f6: 6; +>f6 : Symbol(f6, Decl(ambient.d.ts, 40, 16)) + + export const f7: 7; +>f7 : Symbol(f7, Decl(ambient.d.ts, 41, 16)) + + export const f8: 8; +>f8 : Symbol(f8, Decl(ambient.d.ts, 42, 16)) + + export const f9: 9; +>f9 : Symbol(f9, Decl(ambient.d.ts, 43, 16)) + + export const f10: 10; +>f10 : Symbol(f10, Decl(ambient.d.ts, 44, 16)) +} +declare module "mm" { +>"mm" : Symbol("mm", Decl(ambient.d.ts, 45, 1)) + + import t = require("mt"); +>t : Symbol(t, Decl(ambient.d.ts, 46, 21)) + + export = t; +>t : Symbol(t, Decl(ambient.d.ts, 46, 21)) + + export const C: unique symbol; +>C : Symbol(C, Decl(ambient.d.ts, 49, 16)) + + global { +>global : Symbol(global, Decl(ambient.d.ts, 49, 34)) + + const gc: { c: typeof C }; +>gc : Symbol(gc, Decl(ambient.d.ts, 51, 13)) +>c : Symbol(c, Decl(ambient.d.ts, 51, 19)) +>C : Symbol(C, Decl(ambient.d.ts, 49, 16)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types new file mode 100644 index 0000000000000..6e3734d808ac2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types @@ -0,0 +1,173 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts] //// + +=== /main.ts === +/// +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const w = gc; +>w : { c: typeof import("mm").C; } +>gc : { c: typeof import("mm").C; } + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "mt" { +>"mt" : typeof import("mt") + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export const f1: 1; +>f1 : 1 + + export const f2: 2; +>f2 : 2 + + export const f3: 3; +>f3 : 3 + + export const f4: 4; +>f4 : 4 + + export const f5: 5; +>f5 : 5 + + export const f6: 6; +>f6 : 6 + + export const f7: 7; +>f7 : 7 + + export const f8: 8; +>f8 : 8 + + export const f9: 9; +>f9 : 9 + + export const f10: 10; +>f10 : 10 +} +declare module "mm" { +>"mm" : typeof import("mm") + + import t = require("mt"); +>t : typeof t + + export = t; +>t : typeof t + + export const C: unique symbol; +>C : unique symbol + + global { +>global : typeof global + + const gc: { c: typeof C }; +>gc : { c: typeof C; } +>c : unique symbol +>C : unique symbol + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js new file mode 100644 index 0000000000000..b04d7f8d88c86 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +//// [index.d.ts] +export { v as a } from "ia"; +export { v as b } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; + +//// [gadget.d.ts] +export interface Gadget { id: number } + +//// [widget.d.ts] +import { Gadget } from "./gadget"; +export declare function makeWidget(): Gadget; + +//// [main.ts] +/// +import { f1 } from "./index"; +import { makeWidget } from "./widget"; +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const w = makeWidget(); +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const keep: 1; +export declare const w: import("./gadget").Gadget; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.symbols new file mode 100644 index 0000000000000..7750beccf74ec --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.symbols @@ -0,0 +1,180 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "./index"; +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +import { makeWidget } from "./widget"; +>makeWidget : Symbol(makeWidget, Decl(main.ts, 2, 8)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 2, 38)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) + +export const keep = f1; +>keep : Symbol(keep, Decl(main.ts, 4, 12)) +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +export const w = makeWidget(); +>w : Symbol(w, Decl(main.ts, 5, 12)) +>makeWidget : Symbol(makeWidget, Decl(main.ts, 2, 8)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 38)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 7, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 38)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + +=== /index.d.ts === +export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(index.d.ts, 0, 8)) + +export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(index.d.ts, 1, 8)) + +export declare const f1: 1; +>f1 : Symbol(f1, Decl(index.d.ts, 2, 20)) + +export declare const f2: 2; +>f2 : Symbol(f2, Decl(index.d.ts, 3, 20)) + +export declare const f3: 3; +>f3 : Symbol(f3, Decl(index.d.ts, 4, 20)) + +export declare const f4: 4; +>f4 : Symbol(f4, Decl(index.d.ts, 5, 20)) + +export declare const f5: 5; +>f5 : Symbol(f5, Decl(index.d.ts, 6, 20)) + +export declare const f6: 6; +>f6 : Symbol(f6, Decl(index.d.ts, 7, 20)) + +export declare const f7: 7; +>f7 : Symbol(f7, Decl(index.d.ts, 8, 20)) + +export declare const f8: 8; +>f8 : Symbol(f8, Decl(index.d.ts, 9, 20)) + +export declare const f9: 9; +>f9 : Symbol(f9, Decl(index.d.ts, 10, 20)) + +export declare const f10: 10; +>f10 : Symbol(f10, Decl(index.d.ts, 11, 20)) + +=== /gadget.d.ts === +export interface Gadget { id: number } +>Gadget : Symbol(Gadget, Decl(gadget.d.ts, 0, 0)) +>id : Symbol(Gadget.id, Decl(gadget.d.ts, 0, 25)) + +=== /widget.d.ts === +import { Gadget } from "./gadget"; +>Gadget : Symbol(Gadget, Decl(widget.d.ts, 0, 8)) + +export declare function makeWidget(): Gadget; +>makeWidget : Symbol(makeWidget, Decl(widget.d.ts, 0, 34)) +>Gadget : Symbol(Gadget, Decl(widget.d.ts, 0, 8)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types new file mode 100644 index 0000000000000..351c0f5869478 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types @@ -0,0 +1,164 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "./index"; +>f1 : 1 + +import { makeWidget } from "./widget"; +>makeWidget : () => import("./gadget").Gadget + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const keep = f1; +>keep : 1 +>f1 : 1 + +export const w = makeWidget(); +>w : import("./gadget").Gadget +>makeWidget() : import("./gadget").Gadget +>makeWidget : () => import("./gadget").Gadget + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + +=== /index.d.ts === +export { v as a } from "ia"; +>v : 0 +>a : 0 + +export { v as b } from "ib"; +>v : 0 +>b : 0 + +export declare const f1: 1; +>f1 : 1 + +export declare const f2: 2; +>f2 : 2 + +export declare const f3: 3; +>f3 : 3 + +export declare const f4: 4; +>f4 : 4 + +export declare const f5: 5; +>f5 : 5 + +export declare const f6: 6; +>f6 : 6 + +export declare const f7: 7; +>f7 : 7 + +export declare const f8: 8; +>f8 : 8 + +export declare const f9: 9; +>f9 : 9 + +export declare const f10: 10; +>f10 : 10 + +=== /gadget.d.ts === +export interface Gadget { id: number } +>id : number + +=== /widget.d.ts === +import { Gadget } from "./gadget"; +>Gadget : any + +export declare function makeWidget(): Gadget; +>makeWidget : () => Gadget + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js new file mode 100644 index 0000000000000..da41b8a0dc439 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +//// [main.ts] +/// +declare const k: string; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export class C { + [k]: unknown; + sa = { ...(null! as typeof import("ma")) }; + sb = { ...(null! as typeof import("mb")) }; + f1 = 1; + f2 = 2; + f3 = 3; + f4 = 4; + f5 = 5; + f6 = 6; + f7 = 7; + f8 = 8; + f9 = 9; + f10 = 10; +} +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +declare const k: string; +export declare class C { + [k]: unknown; + sa: { + pa: 0; + ma: 1; + }; + sb: { + pb: 0; + mb: 1; + }; + f1: number; + f2: number; + f3: number; + f4: number; + f5: number; + f6: number; + f7: number; + f8: number; + f9: number; + f10: number; +} +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.symbols new file mode 100644 index 0000000000000..58a51ee5e09e3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.symbols @@ -0,0 +1,168 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts] //// + +=== /main.ts === +/// +declare const k: string; +>k : Symbol(k, Decl(main.ts, 1, 13)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) + +export class C { +>C : Symbol(C, Decl(main.ts, 4, 55)) + + [k]: unknown; +>[k] : Symbol(C[k], Decl(main.ts, 5, 16)) +>k : Symbol(k, Decl(main.ts, 1, 13)) + + sa = { ...(null! as typeof import("ma")) }; +>sa : Symbol(C.sa, Decl(main.ts, 6, 17)) + + sb = { ...(null! as typeof import("mb")) }; +>sb : Symbol(C.sb, Decl(main.ts, 7, 47)) + + f1 = 1; +>f1 : Symbol(C.f1, Decl(main.ts, 8, 47)) + + f2 = 2; +>f2 : Symbol(C.f2, Decl(main.ts, 9, 11)) + + f3 = 3; +>f3 : Symbol(C.f3, Decl(main.ts, 10, 11)) + + f4 = 4; +>f4 : Symbol(C.f4, Decl(main.ts, 11, 11)) + + f5 = 5; +>f5 : Symbol(C.f5, Decl(main.ts, 12, 11)) + + f6 = 6; +>f6 : Symbol(C.f6, Decl(main.ts, 13, 11)) + + f7 = 7; +>f7 : Symbol(C.f7, Decl(main.ts, 14, 11)) + + f8 = 8; +>f8 : Symbol(C.f8, Decl(main.ts, 15, 11)) + + f9 = 9; +>f9 : Symbol(C.f9, Decl(main.ts, 16, 11)) + + f10 = 10; +>f10 : Symbol(C.f10, Decl(main.ts, 17, 11)) +} +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 20, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 21, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types new file mode 100644 index 0000000000000..9ca145132f072 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types @@ -0,0 +1,171 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts] //// + +=== /main.ts === +/// +declare const k: string; +>k : string + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export class C { +>C : C + + [k]: unknown; +>[k] : unknown +>k : string + + sa = { ...(null! as typeof import("ma")) }; +>sa : { pa: 0; ma: 1; } +>{ ...(null! as typeof import("ma")) } : { pa: 0; ma: 1; } +>(null! as typeof import("ma")) : typeof import("ma") +>null! as typeof import("ma") : typeof import("ma") +>null! : never + + sb = { ...(null! as typeof import("mb")) }; +>sb : { pb: 0; mb: 1; } +>{ ...(null! as typeof import("mb")) } : { pb: 0; mb: 1; } +>(null! as typeof import("mb")) : typeof import("mb") +>null! as typeof import("mb") : typeof import("mb") +>null! : never + + f1 = 1; +>f1 : number +>1 : 1 + + f2 = 2; +>f2 : number +>2 : 2 + + f3 = 3; +>f3 : number +>3 : 3 + + f4 = 4; +>f4 : number +>4 : 4 + + f5 = 5; +>f5 : number +>5 : 5 + + f6 = 6; +>f6 : number +>6 : 6 + + f7 = 7; +>f7 : number +>7 : 7 + + f8 = 8; +>f8 : number +>8 : 8 + + f9 = 9; +>f9 : number +>9 : 9 + + f10 = 10; +>f10 : number +>10 : 10 +} +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js new file mode 100644 index 0000000000000..ea5bdabd550b2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js @@ -0,0 +1,79 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +//// [globals.d.ts] +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); + +//// [lib.d.ts] +interface X { x: 1 } +export { X as Y }; +export { v as a } from "ia"; +export { v as b } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; +export declare function getX(): X; + +//// [main.ts] +/// +/// +import { getX } from "./lib"; +declare function expand(): { [K in keyof X]: X[K] }; +export const x = getX(); +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const x: import("./lib").Y; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.symbols new file mode 100644 index 0000000000000..0c99a97d3064b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.symbols @@ -0,0 +1,182 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts] //// + +=== /main.ts === +/// +/// +import { getX } from "./lib"; +>getX : Symbol(getX, Decl(main.ts, 2, 8)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 2, 29)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) + +export const x = getX(); +>x : Symbol(x, Decl(main.ts, 4, 12)) +>getX : Symbol(getX, Decl(main.ts, 2, 8)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 5, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 29)) +>oxa : Symbol(oxa, Decl(globals.d.ts, 0, 13)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 2, 29)) +>oxb : Symbol(oxb, Decl(globals.d.ts, 1, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + +=== /globals.d.ts === +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(globals.d.ts, 0, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(globals.d.ts, 1, 13)) + +=== /lib.d.ts === +interface X { x: 1 } +>X : Symbol(X, Decl(lib.d.ts, --, --)) +>x : Symbol(X.x, Decl(lib.d.ts, --, --)) + +export { X as Y }; +>X : Symbol(X, Decl(lib.d.ts, --, --)) +>Y : Symbol(Y, Decl(lib.d.ts, --, --)) + +export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(lib.d.ts, --, --)) + +export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(lib.d.ts, --, --)) + +export declare const f1: 1; +>f1 : Symbol(f1, Decl(lib.d.ts, --, --)) + +export declare const f2: 2; +>f2 : Symbol(f2, Decl(lib.d.ts, --, --)) + +export declare const f3: 3; +>f3 : Symbol(f3, Decl(lib.d.ts, --, --)) + +export declare const f4: 4; +>f4 : Symbol(f4, Decl(lib.d.ts, --, --)) + +export declare const f5: 5; +>f5 : Symbol(f5, Decl(lib.d.ts, --, --)) + +export declare const f6: 6; +>f6 : Symbol(f6, Decl(lib.d.ts, --, --)) + +export declare const f7: 7; +>f7 : Symbol(f7, Decl(lib.d.ts, --, --)) + +export declare const f8: 8; +>f8 : Symbol(f8, Decl(lib.d.ts, --, --)) + +export declare const f9: 9; +>f9 : Symbol(f9, Decl(lib.d.ts, --, --)) + +export declare const f10: 10; +>f10 : Symbol(f10, Decl(lib.d.ts, --, --)) + +export declare function getX(): X; +>getX : Symbol(getX, Decl(lib.d.ts, --, --)) +>X : Symbol(X, Decl(lib.d.ts, --, --)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types new file mode 100644 index 0000000000000..ad400806b641d --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types @@ -0,0 +1,166 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts] //// + +=== /main.ts === +/// +/// +import { getX } from "./lib"; +>getX : () => import("./lib").Y + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const x = getX(); +>x : import("./lib").Y +>getX() : import("./lib").Y +>getX : () => import("./lib").Y + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + +=== /globals.d.ts === +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +=== /lib.d.ts === +interface X { x: 1 } +>x : 1 + +export { X as Y }; +>X : any +>Y : any + +export { v as a } from "ia"; +>v : 0 +>a : 0 + +export { v as b } from "ib"; +>v : 0 +>b : 0 + +export declare const f1: 1; +>f1 : 1 + +export declare const f2: 2; +>f2 : 2 + +export declare const f3: 3; +>f3 : 3 + +export declare const f4: 4; +>f4 : 4 + +export declare const f5: 5; +>f5 : 5 + +export declare const f6: 6; +>f6 : 6 + +export declare const f7: 7; +>f7 : 7 + +export declare const f8: 8; +>f8 : 8 + +export declare const f9: 9; +>f9 : 9 + +export declare const f10: 10; +>f10 : 10 + +export declare function getX(): X; +>getX : () => X + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js new file mode 100644 index 0000000000000..f297255bfad65 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js @@ -0,0 +1,143 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mm" { + export const C: unique symbol; + global { + const gc: { c: typeof C }; + } +} + +//// [ua.d.ts] +import { v } from "ia"; +export = v; +export as namespace UA; + +//// [ub.d.ts] +import { v } from "ib"; +export = v; +export as namespace UB; + +//// [uf1.d.ts] +declare const x: 1; +export = x; +export as namespace UF1; + +//// [uf2.d.ts] +declare const x: 2; +export = x; +export as namespace UF2; + +//// [uf3.d.ts] +declare const x: 3; +export = x; +export as namespace UF3; + +//// [uf4.d.ts] +declare const x: 4; +export = x; +export as namespace UF4; + +//// [uf5.d.ts] +declare const x: 5; +export = x; +export as namespace UF5; + +//// [uf6.d.ts] +declare const x: 6; +export = x; +export as namespace UF6; + +//// [uf7.d.ts] +declare const x: 7; +export = x; +export as namespace UF7; + +//// [uf8.d.ts] +declare const x: 8; +export = x; +export as namespace UF8; + +//// [uf9.d.ts] +declare const x: 9; +export = x; +export as namespace UF9; + +//// [uf10.d.ts] +declare const x: 10; +export = x; +export as namespace UF10; + +//// [main.ts] +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +const w = gc; +const obsA = expand(); +const obsB = expand(); + + + + +//// [main.d.ts] +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { + [K in keyof X]: X[K]; +}; +declare const w: { + c: typeof import("mm").C; +}; +declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.symbols new file mode 100644 index 0000000000000..7613d3b6c6cc6 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.symbols @@ -0,0 +1,273 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts] //// + +=== /main.ts === +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 13, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 14, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>K : Symbol(K, Decl(main.ts, 15, 33)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>K : Symbol(K, Decl(main.ts, 15, 33)) + +const w = gc; +>w : Symbol(w, Decl(main.ts, 16, 5)) +>gc : Symbol(gc, Decl(ambient.d.ts, 35, 13)) + +const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 17, 5)) +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 13, 13)) + +const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 18, 5)) +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 14, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(UA, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(UB, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "mm" { +>"mm" : Symbol("mm", Decl(ambient.d.ts, 31, 1)) + + export const C: unique symbol; +>C : Symbol(C, Decl(ambient.d.ts, 33, 16)) + + global { +>global : Symbol(global, Decl(ambient.d.ts, 33, 34)) + + const gc: { c: typeof C }; +>gc : Symbol(gc, Decl(ambient.d.ts, 35, 13)) +>c : Symbol(c, Decl(ambient.d.ts, 35, 19)) +>C : Symbol(C, Decl(ambient.d.ts, 33, 16)) + } +} + +=== /ua.d.ts === +import { v } from "ia"; +>v : Symbol(v, Decl(ua.d.ts, 0, 8)) + +export = v; +>v : Symbol(v, Decl(ua.d.ts, 0, 8)) + +export as namespace UA; +>UA : Symbol(UA, Decl(ua.d.ts, 1, 11)) + +=== /ub.d.ts === +import { v } from "ib"; +>v : Symbol(v, Decl(ub.d.ts, 0, 8)) + +export = v; +>v : Symbol(v, Decl(ub.d.ts, 0, 8)) + +export as namespace UB; +>UB : Symbol(UB, Decl(ub.d.ts, 1, 11)) + +=== /uf1.d.ts === +declare const x: 1; +>x : Symbol(x, Decl(uf1.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf1.d.ts, 0, 13)) + +export as namespace UF1; +>UF1 : Symbol(UF1, Decl(uf1.d.ts, 1, 11)) + +=== /uf2.d.ts === +declare const x: 2; +>x : Symbol(x, Decl(uf2.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf2.d.ts, 0, 13)) + +export as namespace UF2; +>UF2 : Symbol(UF2, Decl(uf2.d.ts, 1, 11)) + +=== /uf3.d.ts === +declare const x: 3; +>x : Symbol(x, Decl(uf3.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf3.d.ts, 0, 13)) + +export as namespace UF3; +>UF3 : Symbol(UF3, Decl(uf3.d.ts, 1, 11)) + +=== /uf4.d.ts === +declare const x: 4; +>x : Symbol(x, Decl(uf4.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf4.d.ts, 0, 13)) + +export as namespace UF4; +>UF4 : Symbol(UF4, Decl(uf4.d.ts, 1, 11)) + +=== /uf5.d.ts === +declare const x: 5; +>x : Symbol(x, Decl(uf5.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf5.d.ts, 0, 13)) + +export as namespace UF5; +>UF5 : Symbol(UF5, Decl(uf5.d.ts, 1, 11)) + +=== /uf6.d.ts === +declare const x: 6; +>x : Symbol(x, Decl(uf6.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf6.d.ts, 0, 13)) + +export as namespace UF6; +>UF6 : Symbol(UF6, Decl(uf6.d.ts, 1, 11)) + +=== /uf7.d.ts === +declare const x: 7; +>x : Symbol(x, Decl(uf7.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf7.d.ts, 0, 13)) + +export as namespace UF7; +>UF7 : Symbol(UF7, Decl(uf7.d.ts, 1, 11)) + +=== /uf8.d.ts === +declare const x: 8; +>x : Symbol(x, Decl(uf8.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf8.d.ts, 0, 13)) + +export as namespace UF8; +>UF8 : Symbol(UF8, Decl(uf8.d.ts, 1, 11)) + +=== /uf9.d.ts === +declare const x: 9; +>x : Symbol(x, Decl(uf9.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf9.d.ts, 0, 13)) + +export as namespace UF9; +>UF9 : Symbol(UF9, Decl(uf9.d.ts, 1, 11)) + +=== /uf10.d.ts === +declare const x: 10; +>x : Symbol(x, Decl(uf10.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf10.d.ts, 0, 13)) + +export as namespace UF10; +>UF10 : Symbol(UF10, Decl(uf10.d.ts, 1, 11)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types new file mode 100644 index 0000000000000..cb6e79ef1518c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types @@ -0,0 +1,258 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts] //// + +=== /main.ts === +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +const w = gc; +>w : { c: typeof import("mm").C; } +>gc : { c: typeof import("mm").C; } + +const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "mm" { +>"mm" : typeof import("mm") + + export const C: unique symbol; +>C : unique symbol + + global { +>global : typeof global + + const gc: { c: typeof C }; +>gc : { c: typeof C; } +>c : unique symbol +>C : unique symbol + } +} + +=== /ua.d.ts === +import { v } from "ia"; +>v : 0 + +export = v; +>v : 0 + +export as namespace UA; +>UA : 0 + +=== /ub.d.ts === +import { v } from "ib"; +>v : 0 + +export = v; +>v : 0 + +export as namespace UB; +>UB : 0 + +=== /uf1.d.ts === +declare const x: 1; +>x : 1 + +export = x; +>x : 1 + +export as namespace UF1; +>UF1 : 1 + +=== /uf2.d.ts === +declare const x: 2; +>x : 2 + +export = x; +>x : 2 + +export as namespace UF2; +>UF2 : 2 + +=== /uf3.d.ts === +declare const x: 3; +>x : 3 + +export = x; +>x : 3 + +export as namespace UF3; +>UF3 : 3 + +=== /uf4.d.ts === +declare const x: 4; +>x : 4 + +export = x; +>x : 4 + +export as namespace UF4; +>UF4 : 4 + +=== /uf5.d.ts === +declare const x: 5; +>x : 5 + +export = x; +>x : 5 + +export as namespace UF5; +>UF5 : 5 + +=== /uf6.d.ts === +declare const x: 6; +>x : 6 + +export = x; +>x : 6 + +export as namespace UF6; +>UF6 : 6 + +=== /uf7.d.ts === +declare const x: 7; +>x : 7 + +export = x; +>x : 7 + +export as namespace UF7; +>UF7 : 7 + +=== /uf8.d.ts === +declare const x: 8; +>x : 8 + +export = x; +>x : 8 + +export as namespace UF8; +>UF8 : 8 + +=== /uf9.d.ts === +declare const x: 9; +>x : 9 + +export = x; +>x : 9 + +export as namespace UF9; +>UF9 : 9 + +=== /uf10.d.ts === +declare const x: 10; +>x : 10 + +export = x; +>x : 10 + +export as namespace UF10; +>UF10 : 10 + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js new file mode 100644 index 0000000000000..30a76df171c66 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +//// [globals.d.ts] +declare const gb: { [K in keyof typeof import("ma")]: 0 }; +declare const ga: { [K in keyof typeof import("mb")]: 0 }; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); + +//// [keys.d.ts] +interface Keys { readonly key: unique symbol } +declare const keys: Keys; +declare function mk(): { [keys.key]: 1 }; + +//// [main.ts] +/// +/// +/// +declare function expand(): { [K in keyof X]: X[K] }; +export const obj = mk(); +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const obj: { + [Keys.key]: 1; +}; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.symbols new file mode 100644 index 0000000000000..39cfe1287f026 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.symbols @@ -0,0 +1,117 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts] //// + +=== /main.ts === +/// +/// +/// +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 0, 0)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>X : Symbol(X, Decl(main.ts, 3, 24)) +>K : Symbol(K, Decl(main.ts, 3, 33)) + +export const obj = mk(); +>obj : Symbol(obj, Decl(main.ts, 4, 12)) +>mk : Symbol(mk, Decl(keys.d.ts, 1, 25)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 5, 12)) +>expand : Symbol(expand, Decl(main.ts, 0, 0)) +>oxa : Symbol(oxa, Decl(globals.d.ts, 2, 13)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 0, 0)) +>oxb : Symbol(oxb, Decl(globals.d.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} + +=== /globals.d.ts === +declare const gb: { [K in keyof typeof import("ma")]: 0 }; +>gb : Symbol(gb, Decl(globals.d.ts, 0, 13)) +>K : Symbol(K, Decl(globals.d.ts, 0, 21)) + +declare const ga: { [K in keyof typeof import("mb")]: 0 }; +>ga : Symbol(ga, Decl(globals.d.ts, 1, 13)) +>K : Symbol(K, Decl(globals.d.ts, 1, 21)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(globals.d.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(globals.d.ts, 3, 13)) + +=== /keys.d.ts === +interface Keys { readonly key: unique symbol } +>Keys : Symbol(Keys, Decl(keys.d.ts, 0, 0)) +>key : Symbol(Keys.key, Decl(keys.d.ts, 0, 16)) + +declare const keys: Keys; +>keys : Symbol(keys, Decl(keys.d.ts, 1, 13)) +>Keys : Symbol(Keys, Decl(keys.d.ts, 0, 0)) + +declare function mk(): { [keys.key]: 1 }; +>mk : Symbol(mk, Decl(keys.d.ts, 1, 25)) +>[keys.key] : Symbol([keys.key], Decl(keys.d.ts, 2, 24)) +>keys.key : Symbol(Keys.key, Decl(keys.d.ts, 0, 16)) +>keys : Symbol(keys, Decl(keys.d.ts, 1, 13)) +>key : Symbol(Keys.key, Decl(keys.d.ts, 0, 16)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types new file mode 100644 index 0000000000000..2e41e20ad32be --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types @@ -0,0 +1,105 @@ +//// [tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts] //// + +=== /main.ts === +/// +/// +/// +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const obj = mk(); +>obj : { [Keys.key]: 1; } +>mk() : { [Keys.key]: 1; } +>mk : () => { [keys.key]: 1; } + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + +=== /globals.d.ts === +declare const gb: { [K in keyof typeof import("ma")]: 0 }; +>gb : { pa: 0; readonly ma: 0; } + +declare const ga: { [K in keyof typeof import("mb")]: 0 }; +>ga : { pb: 0; readonly mb: 0; } + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +=== /keys.d.ts === +interface Keys { readonly key: unique symbol } +>key : unique symbol + +declare const keys: Keys; +>keys : Keys + +declare function mk(): { [keys.key]: 1 }; +>mk : () => { [keys.key]: 1; } +>[keys.key] : 1 +>keys.key : unique symbol +>keys : Keys +>key : unique symbol + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.errors.txt new file mode 100644 index 0000000000000..2d329fab99108 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.errors.txt @@ -0,0 +1,13 @@ +noFlakyDiagnosticsBindingPatternInFunctionType1.ts(2,40): error TS2842: 'boolean' is an unused renaming of 'a'. Did you intend to use it as a type annotation? +noFlakyDiagnosticsBindingPatternInFunctionType1.ts(2,40): error TS7031: Binding element 'boolean' implicitly has an 'any' type. + + +==== noFlakyDiagnosticsBindingPatternInFunctionType1.ts (2 errors) ==== + class Base {} + export class C extends Base<{ e: ({ a: boolean }) => void }> {} + ~~~~~~~ +!!! error TS2842: 'boolean' is an unused renaming of 'a'. Did you intend to use it as a type annotation? +!!! related TS2843 noFlakyDiagnosticsBindingPatternInFunctionType1.ts:2:49: We can only write a type for 'a' by adding a type for the entire parameter here. + ~~~~~~~ +!!! error TS7031: Binding element 'boolean' implicitly has an 'any' type. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.js new file mode 100644 index 0000000000000..551d477ce6f8a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.js @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts] //// + +//// [noFlakyDiagnosticsBindingPatternInFunctionType1.ts] +class Base {} +export class C extends Base<{ e: ({ a: boolean }) => void }> {} + + +//// [noFlakyDiagnosticsBindingPatternInFunctionType1.js] +class Base { +} +export class C extends Base { +} + + +//// [noFlakyDiagnosticsBindingPatternInFunctionType1.d.ts] +declare class Base { +} +export declare class C extends Base<{ + e: ({ a: boolean }: { + a: any; + }) => void; +}> { +} +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.symbols new file mode 100644 index 0000000000000..65f18aa411f2a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.symbols @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts] //// + +=== noFlakyDiagnosticsBindingPatternInFunctionType1.ts === +class Base {} +>Base : Symbol(Base, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 0, 0)) +>T : Symbol(T, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 0, 11)) + +export class C extends Base<{ e: ({ a: boolean }) => void }> {} +>C : Symbol(C, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 0, 16)) +>Base : Symbol(Base, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 0, 0)) +>e : Symbol(e, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 1, 29)) +>a : Symbol(a) +>boolean : Symbol(boolean, Decl(noFlakyDiagnosticsBindingPatternInFunctionType1.ts, 1, 35)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.types new file mode 100644 index 0000000000000..f878abfce4262 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.types @@ -0,0 +1,13 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts] //// + +=== noFlakyDiagnosticsBindingPatternInFunctionType1.ts === +class Base {} +>Base : Base + +export class C extends Base<{ e: ({ a: boolean }) => void }> {} +>C : C +>Base : Base<{ e: ({ a: boolean }: any) => void; }> +>e : ({ a: boolean }: { a: any; }) => void +>a : any +>boolean : any + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.errors.txt new file mode 100644 index 0000000000000..71af449eba9aa --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.errors.txt @@ -0,0 +1,18 @@ +/a.js(3,33): error TS1005: ';' expected. +/a.js(3,39): error TS2842: 'boolean' is an unused renaming of 'a'. Did you intend to use it as a type annotation? +/a.js(3,39): error TS7031: Binding element 'boolean' implicitly has an 'any' type. + + +==== /a.js (3 errors) ==== + /** @template T */ + class Base {} + /** @extends {Base<{ e: function({ a: boolean }): void }>} */ + ~ +!!! error TS1005: ';' expected. + ~~~~~~~ +!!! error TS2842: 'boolean' is an unused renaming of 'a'. Did you intend to use it as a type annotation? +!!! related TS2843 /a.js:3:48: We can only write a type for 'a' by adding a type for the entire parameter here. + ~~~~~~~ +!!! error TS7031: Binding element 'boolean' implicitly has an 'any' type. + export class C extends Base {} + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.js new file mode 100644 index 0000000000000..387cc2b27b8c5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.js @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts] //// + +//// [a.js] +/** @template T */ +class Base {} +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export class C extends Base {} + + +//// [a.js] +/** @template T */ +class Base { +} +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export class C extends Base { +} + + +//// [a.d.ts] +/** @template T */ +declare class Base { +} +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export declare class C extends Base<{ + e: Function; + ({ a: boolean }: { + a: any; + }): void; +}> { +} +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.symbols new file mode 100644 index 0000000000000..8af6cff51ce16 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.symbols @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts] //// + +=== /a.js === +/** @template T */ +class Base {} +>Base : Symbol(Base, Decl(a.js, 0, 0)) + +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export class C extends Base {} +>C : Symbol(C, Decl(a.js, 1, 13)) +>Base : Symbol(Base, Decl(a.js, 0, 0)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.types new file mode 100644 index 0000000000000..b7301a9017d21 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.types @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts] //// + +=== /a.js === +/** @template T */ +class Base {} +>Base : Base + +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export class C extends Base {} +>C : C +>Base : Base<{ ({ a: boolean }: { a: any; }): void; e: Function; }> + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt new file mode 100644 index 0000000000000..a2ecba905fe18 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt @@ -0,0 +1,84 @@ +/main.ts(7,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. +/main.ts(8,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + + +==== /main.ts (2 errors) ==== + /// + import { f1 } from "m1"; + declare const oxa: typeof import("xa"); + declare const oxb: typeof import("xb"); + declare function expand(): { [K in keyof X]: X[K] }; + export const keep = f1; + export const obsA: never = expand(); + ~~~~ +!!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. + export const obsB: never = expand(); + ~~~~ +!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + +==== /ambient.d.ts (0 errors) ==== + declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; + } + declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; + } + declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "s1" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + } + declare module "s2" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + } + declare module "m1" { + export * from "s1"; + export * from "s2"; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.symbols new file mode 100644 index 0000000000000..5ca84074f7a11 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.symbols @@ -0,0 +1,217 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsExportStarCollisionOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "m1"; +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) + +export const keep = f1; +>keep : Symbol(keep, Decl(main.ts, 5, 12)) +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +export const obsA: never = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +export const obsB: never = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 7, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "s1" { +>"s1" : Symbol("s1", Decl(ambient.d.ts, 31, 1)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 33, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 34, 12)) + + export const f1: 1; +>f1 : Symbol(f1, Decl(ambient.d.ts, 35, 16)) + + export const f2: 2; +>f2 : Symbol(f2, Decl(ambient.d.ts, 36, 16)) + + export const f3: 3; +>f3 : Symbol(f3, Decl(ambient.d.ts, 37, 16)) + + export const f4: 4; +>f4 : Symbol(f4, Decl(ambient.d.ts, 38, 16)) + + export const f5: 5; +>f5 : Symbol(f5, Decl(ambient.d.ts, 39, 16)) + + export const f6: 6; +>f6 : Symbol(f6, Decl(ambient.d.ts, 40, 16)) + + export const f7: 7; +>f7 : Symbol(f7, Decl(ambient.d.ts, 41, 16)) + + export const f8: 8; +>f8 : Symbol(f8, Decl(ambient.d.ts, 42, 16)) + + export const f9: 9; +>f9 : Symbol(f9, Decl(ambient.d.ts, 43, 16)) + + export const f10: 10; +>f10 : Symbol(f10, Decl(ambient.d.ts, 44, 16)) +} +declare module "s2" { +>"s2" : Symbol("s2", Decl(ambient.d.ts, 45, 1)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 47, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 48, 12)) + + export const f1: 1; +>f1 : Symbol(f1, Decl(ambient.d.ts, 49, 16)) + + export const f2: 2; +>f2 : Symbol(f2, Decl(ambient.d.ts, 50, 16)) + + export const f3: 3; +>f3 : Symbol(f3, Decl(ambient.d.ts, 51, 16)) + + export const f4: 4; +>f4 : Symbol(f4, Decl(ambient.d.ts, 52, 16)) + + export const f5: 5; +>f5 : Symbol(f5, Decl(ambient.d.ts, 53, 16)) + + export const f6: 6; +>f6 : Symbol(f6, Decl(ambient.d.ts, 54, 16)) + + export const f7: 7; +>f7 : Symbol(f7, Decl(ambient.d.ts, 55, 16)) + + export const f8: 8; +>f8 : Symbol(f8, Decl(ambient.d.ts, 56, 16)) + + export const f9: 9; +>f9 : Symbol(f9, Decl(ambient.d.ts, 57, 16)) + + export const f10: 10; +>f10 : Symbol(f10, Decl(ambient.d.ts, 58, 16)) +} +declare module "m1" { +>"m1" : Symbol("m1", Decl(ambient.d.ts, 59, 1)) + + export * from "s1"; + export * from "s2"; +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types new file mode 100644 index 0000000000000..925b972b915c6 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types @@ -0,0 +1,202 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsExportStarCollisionOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "m1"; +>f1 : 1 + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const keep = f1; +>keep : 1 +>f1 : 1 + +export const obsA: never = expand(); +>obsA : never +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB: never = expand(); +>obsB : never +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "s1" { +>"s1" : typeof import("s1") + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export const f1: 1; +>f1 : 1 + + export const f2: 2; +>f2 : 2 + + export const f3: 3; +>f3 : 3 + + export const f4: 4; +>f4 : 4 + + export const f5: 5; +>f5 : 5 + + export const f6: 6; +>f6 : 6 + + export const f7: 7; +>f7 : 7 + + export const f8: 8; +>f8 : 8 + + export const f9: 9; +>f9 : 9 + + export const f10: 10; +>f10 : 10 +} +declare module "s2" { +>"s2" : typeof import("s2") + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export const f1: 1; +>f1 : 1 + + export const f2: 2; +>f2 : 2 + + export const f3: 3; +>f3 : 3 + + export const f4: 4; +>f4 : 4 + + export const f5: 5; +>f5 : 5 + + export const f6: 6; +>f6 : 6 + + export const f7: 7; +>f7 : 7 + + export const f8: 8; +>f8 : 8 + + export const f9: 9; +>f9 : 9 + + export const f10: 10; +>f10 : 10 +} +declare module "m1" { +>"m1" : typeof import("m1") + + export * from "s1"; + export * from "s2"; +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt new file mode 100644 index 0000000000000..a05bda29acf17 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt @@ -0,0 +1,84 @@ +/main.ts(7,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. +/main.ts(8,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + + +==== /main.ts (2 errors) ==== + /// + import { f1 } from "m1"; + declare const oxa: typeof import("xa"); + declare const oxb: typeof import("xb"); + declare function expand(): { [K in keyof X]: X[K] }; + export const keep = f1; + export const obsA: never = expand(); + ~~~~ +!!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. + export const obsB: never = expand(); + ~~~~ +!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + +==== /ambient.d.ts (0 errors) ==== + declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; + } + declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; + } + declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "s1" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + } + declare module "s2" { + export const a: 1; + export const b: 2; + export const g1: 1; + export const g2: 2; + export const g3: 3; + export const g4: 4; + export const g5: 5; + export const g6: 6; + export const g7: 7; + export const g8: 8; + export const g9: 9; + export const g10: 10; + } + declare module "m1" { + export * from "s1"; + export * from "s2"; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.symbols new file mode 100644 index 0000000000000..d98ff5854668f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.symbols @@ -0,0 +1,215 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "m1"; +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) + +export const keep = f1; +>keep : Symbol(keep, Decl(main.ts, 5, 12)) +>f1 : Symbol(f1, Decl(main.ts, 1, 8)) + +export const obsA: never = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +export const obsB: never = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 7, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "s1" { +>"s1" : Symbol("s1", Decl(ambient.d.ts, 31, 1)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 33, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 34, 12)) + + export const f1: 1; +>f1 : Symbol(f1, Decl(ambient.d.ts, 35, 16)) + + export const f2: 2; +>f2 : Symbol(f2, Decl(ambient.d.ts, 36, 16)) + + export const f3: 3; +>f3 : Symbol(f3, Decl(ambient.d.ts, 37, 16)) + + export const f4: 4; +>f4 : Symbol(f4, Decl(ambient.d.ts, 38, 16)) + + export const f5: 5; +>f5 : Symbol(f5, Decl(ambient.d.ts, 39, 16)) + + export const f6: 6; +>f6 : Symbol(f6, Decl(ambient.d.ts, 40, 16)) + + export const f7: 7; +>f7 : Symbol(f7, Decl(ambient.d.ts, 41, 16)) + + export const f8: 8; +>f8 : Symbol(f8, Decl(ambient.d.ts, 42, 16)) + + export const f9: 9; +>f9 : Symbol(f9, Decl(ambient.d.ts, 43, 16)) + + export const f10: 10; +>f10 : Symbol(f10, Decl(ambient.d.ts, 44, 16)) +} +declare module "s2" { +>"s2" : Symbol("s2", Decl(ambient.d.ts, 45, 1)) + + export const a: 1; +>a : Symbol(a, Decl(ambient.d.ts, 47, 16)) + + export const b: 2; +>b : Symbol(b, Decl(ambient.d.ts, 48, 16)) + + export const g1: 1; +>g1 : Symbol(g1, Decl(ambient.d.ts, 49, 16)) + + export const g2: 2; +>g2 : Symbol(g2, Decl(ambient.d.ts, 50, 16)) + + export const g3: 3; +>g3 : Symbol(g3, Decl(ambient.d.ts, 51, 16)) + + export const g4: 4; +>g4 : Symbol(g4, Decl(ambient.d.ts, 52, 16)) + + export const g5: 5; +>g5 : Symbol(g5, Decl(ambient.d.ts, 53, 16)) + + export const g6: 6; +>g6 : Symbol(g6, Decl(ambient.d.ts, 54, 16)) + + export const g7: 7; +>g7 : Symbol(g7, Decl(ambient.d.ts, 55, 16)) + + export const g8: 8; +>g8 : Symbol(g8, Decl(ambient.d.ts, 56, 16)) + + export const g9: 9; +>g9 : Symbol(g9, Decl(ambient.d.ts, 57, 16)) + + export const g10: 10; +>g10 : Symbol(g10, Decl(ambient.d.ts, 58, 16)) +} +declare module "m1" { +>"m1" : Symbol("m1", Decl(ambient.d.ts, 59, 1)) + + export * from "s1"; + export * from "s2"; +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types new file mode 100644 index 0000000000000..58541f8aa49ff --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types @@ -0,0 +1,200 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.ts] //// + +=== /main.ts === +/// +import { f1 } from "m1"; +>f1 : 1 + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const keep = f1; +>keep : 1 +>f1 : 1 + +export const obsA: never = expand(); +>obsA : never +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB: never = expand(); +>obsB : never +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "s1" { +>"s1" : typeof import("s1") + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export const f1: 1; +>f1 : 1 + + export const f2: 2; +>f2 : 2 + + export const f3: 3; +>f3 : 3 + + export const f4: 4; +>f4 : 4 + + export const f5: 5; +>f5 : 5 + + export const f6: 6; +>f6 : 6 + + export const f7: 7; +>f7 : 7 + + export const f8: 8; +>f8 : 8 + + export const f9: 9; +>f9 : 9 + + export const f10: 10; +>f10 : 10 +} +declare module "s2" { +>"s2" : typeof import("s2") + + export const a: 1; +>a : 1 + + export const b: 2; +>b : 2 + + export const g1: 1; +>g1 : 1 + + export const g2: 2; +>g2 : 2 + + export const g3: 3; +>g3 : 3 + + export const g4: 4; +>g4 : 4 + + export const g5: 5; +>g5 : 5 + + export const g6: 6; +>g6 : 6 + + export const g7: 7; +>g7 : 7 + + export const g8: 8; +>g8 : 8 + + export const g9: 9; +>g9 : 9 + + export const g10: 10; +>g10 : 10 +} +declare module "m1" { +>"m1" : typeof import("m1") + + export * from "s1"; + export * from "s2"; +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt new file mode 100644 index 0000000000000..fb286e6d925c7 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt @@ -0,0 +1,61 @@ +/main.ts(8,32): error TS2339: Property 'ma' does not exist on type '{}'. + + +==== /main.ts (1 errors) ==== + import * as idx from "./index"; + const all = { ...idx }; + import * as MA from "./ma"; + import * as MB from "./mb"; + export const fromA = MA.pa; + export const fromB = MB.pb; + export const checkA: 1 = MA.pa.mb; + export const checkB: 1 = MB.pb.ma; + ~~ +!!! error TS2339: Property 'ma' does not exist on type '{}'. + +==== /ma.js (0 errors) ==== + export { v as pa } from "./xa"; + export const ma = 1; + +==== /mb.js (0 errors) ==== + export { v as pb } from "./xb"; + export const mb = 1; + +==== /xa.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /xb.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ia.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ib.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /index.js (0 errors) ==== + export { v as a } from "./ia"; + export { v as b } from "./ib"; + export const f1 = 1; + export const f2 = 2; + export const f3 = 3; + export const f4 = 4; + export const f5 = 5; + export const f6 = 6; + export const f7 = 7; + export const f8 = 8; + export const f9 = 9; + export const f10 = 10; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js new file mode 100644 index 0000000000000..85b0024d3067d --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js @@ -0,0 +1,221 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts] //// + +//// [ma.js] +export { v as pa } from "./xa"; +export const ma = 1; + +//// [mb.js] +export { v as pb } from "./xb"; +export const mb = 1; + +//// [xa.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [xb.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ia.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ib.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [index.js] +export { v as a } from "./ia"; +export { v as b } from "./ib"; +export const f1 = 1; +export const f2 = 2; +export const f3 = 3; +export const f4 = 4; +export const f5 = 5; +export const f6 = 6; +export const f7 = 7; +export const f8 = 8; +export const f9 = 9; +export const f10 = 10; + +//// [main.ts] +import * as idx from "./index"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; + + +//// [xb.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [mb.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mb = exports.pb = void 0; +var xb_1 = require("./xb"); +Object.defineProperty(exports, "pb", { enumerable: true, get: function () { return xb_1.v; } }); +exports.mb = 1; +//// [xa.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ma.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ma = exports.pa = void 0; +var xa_1 = require("./xa"); +Object.defineProperty(exports, "pa", { enumerable: true, get: function () { return xa_1.v; } }); +exports.ma = 1; +//// [ia.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ib.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [index.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.f10 = exports.f9 = exports.f8 = exports.f7 = exports.f6 = exports.f5 = exports.f4 = exports.f3 = exports.f2 = exports.f1 = exports.b = exports.a = void 0; +var ia_1 = require("./ia"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return ia_1.v; } }); +var ib_1 = require("./ib"); +Object.defineProperty(exports, "b", { enumerable: true, get: function () { return ib_1.v; } }); +exports.f1 = 1; +exports.f2 = 2; +exports.f3 = 3; +exports.f4 = 4; +exports.f5 = 5; +exports.f6 = 6; +exports.f7 = 7; +exports.f8 = 8; +exports.f9 = 9; +exports.f10 = 10; +//// [main.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkB = exports.checkA = exports.fromB = exports.fromA = void 0; +const idx = __importStar(require("./index")); +const all = { ...idx }; +const MA = __importStar(require("./ma")); +const MB = __importStar(require("./mb")); +exports.fromA = MA.pa; +exports.fromB = MB.pb; +exports.checkA = MA.pa.mb; +exports.checkB = MB.pb.ma; + + +//// [xb.d.ts] +export = o; +declare const o: { + v: {}; +}; +//// [mb.d.ts] +export { v as pb } from "./xb"; +export declare const mb = 1; +//// [xa.d.ts] +export = o; +declare const o: { + v: { + pb: {}; + mb: 1; + }; +}; +//// [ma.d.ts] +export { v as pa } from "./xa"; +export declare const ma = 1; +//// [ia.d.ts] +export = o; +declare const o: { + v: { + pa: { + pb: {}; + mb: 1; + }; + ma: 1; + }; +}; +//// [ib.d.ts] +export = o; +declare const o: { + v: { + pb: {}; + mb: 1; + }; +}; +//// [index.d.ts] +export { v as a } from "./ia"; +export { v as b } from "./ib"; +export declare const f1 = 1; +export declare const f2 = 2; +export declare const f3 = 3; +export declare const f4 = 4; +export declare const f5 = 5; +export declare const f6 = 6; +export declare const f7 = 7; +export declare const f8 = 8; +export declare const f9 = 9; +export declare const f10 = 10; +//// [main.d.ts] +export declare const fromA: { + pb: {}; + mb: 1; +}; +export declare const fromB: {}; +export declare const checkA: 1; +export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols new file mode 100644 index 0000000000000..aaa25b0469d2f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols @@ -0,0 +1,161 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts] //// + +=== /main.ts === +import * as idx from "./index"; +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +const all = { ...idx }; +>all : Symbol(all, Decl(main.ts, 1, 5)) +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +import * as MA from "./ma"; +>MA : Symbol(MA, Decl(main.ts, 2, 6)) + +import * as MB from "./mb"; +>MB : Symbol(MB, Decl(main.ts, 3, 6)) + +export const fromA = MA.pa; +>fromA : Symbol(fromA, Decl(main.ts, 4, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) + +export const fromB = MB.pb; +>fromB : Symbol(fromB, Decl(main.ts, 5, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) + +export const checkA: 1 = MA.pa.mb; +>checkA : Symbol(checkA, Decl(main.ts, 6, 12)) +>MA.pa.mb : Symbol(mb, Decl(mb.js, 1, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>mb : Symbol(mb, Decl(mb.js, 1, 12)) + +export const checkB: 1 = MB.pb.ma; +>checkB : Symbol(checkB, Decl(main.ts, 7, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) + +=== /ma.js === +export { v as pa } from "./xa"; +>v : Symbol(v, Decl(xa.js, 2, 11)) +>pa : Symbol(pa, Decl(ma.js, 0, 8)) + +export const ma = 1; +>ma : Symbol(ma, Decl(ma.js, 1, 12)) + +=== /mb.js === +export { v as pb } from "./xb"; +>v : Symbol(v, Decl(xb.js, 2, 11)) +>pb : Symbol(pb, Decl(mb.js, 0, 8)) + +export const mb = 1; +>mb : Symbol(mb, Decl(mb.js, 1, 12)) + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xa.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xa.js, 2, 5)) +>v : Symbol(v, Decl(xa.js, 2, 11)) +>M : Symbol(M, Decl(xa.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xa.js, 0, 0)) +>module : Symbol(module, Decl(xa.js, 0, 0)) +>exports : Symbol(exports, Decl(xa.js, 0, 0)) +>o : Symbol(o, Decl(xa.js, 2, 5)) + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xb.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xb.js, 2, 5)) +>v : Symbol(v, Decl(xb.js, 2, 11)) +>M : Symbol(M, Decl(xb.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xb.js, 0, 0)) +>module : Symbol(module, Decl(xb.js, 0, 0)) +>exports : Symbol(exports, Decl(xb.js, 0, 0)) +>o : Symbol(o, Decl(xb.js, 2, 5)) + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ia.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ia.js, 2, 5)) +>v : Symbol(v, Decl(ia.js, 2, 11)) +>M : Symbol(M, Decl(ia.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ia.js, 0, 0)) +>module : Symbol(module, Decl(ia.js, 0, 0)) +>exports : Symbol(exports, Decl(ia.js, 0, 0)) +>o : Symbol(o, Decl(ia.js, 2, 5)) + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ib.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ib.js, 2, 5)) +>v : Symbol(v, Decl(ib.js, 2, 11)) +>M : Symbol(M, Decl(ib.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ib.js, 0, 0)) +>module : Symbol(module, Decl(ib.js, 0, 0)) +>exports : Symbol(exports, Decl(ib.js, 0, 0)) +>o : Symbol(o, Decl(ib.js, 2, 5)) + +=== /index.js === +export { v as a } from "./ia"; +>v : Symbol(v, Decl(ia.js, 2, 11)) +>a : Symbol(a, Decl(index.js, 0, 8)) + +export { v as b } from "./ib"; +>v : Symbol(v, Decl(ib.js, 2, 11)) +>b : Symbol(b, Decl(index.js, 1, 8)) + +export const f1 = 1; +>f1 : Symbol(f1, Decl(index.js, 2, 12)) + +export const f2 = 2; +>f2 : Symbol(f2, Decl(index.js, 3, 12)) + +export const f3 = 3; +>f3 : Symbol(f3, Decl(index.js, 4, 12)) + +export const f4 = 4; +>f4 : Symbol(f4, Decl(index.js, 5, 12)) + +export const f5 = 5; +>f5 : Symbol(f5, Decl(index.js, 6, 12)) + +export const f6 = 6; +>f6 : Symbol(f6, Decl(index.js, 7, 12)) + +export const f7 = 7; +>f7 : Symbol(f7, Decl(index.js, 8, 12)) + +export const f8 = 8; +>f8 : Symbol(f8, Decl(index.js, 9, 12)) + +export const f9 = 9; +>f9 : Symbol(f9, Decl(index.js, 10, 12)) + +export const f10 = 10; +>f10 : Symbol(f10, Decl(index.js, 11, 12)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types new file mode 100644 index 0000000000000..7813095f076be --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types @@ -0,0 +1,192 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts] //// + +=== /main.ts === +import * as idx from "./index"; +>idx : typeof idx + +const all = { ...idx }; +>all : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>idx : typeof idx + +import * as MA from "./ma"; +>MA : typeof MA + +import * as MB from "./mb"; +>MB : typeof MB + +export const fromA = MA.pa; +>fromA : { pb: {}; mb: 1; } +>MA.pa : { pb: {}; mb: 1; } +>MA : typeof MA +>pa : { pb: {}; mb: 1; } + +export const fromB = MB.pb; +>fromB : {} +>MB.pb : {} +>MB : typeof MB +>pb : {} + +export const checkA: 1 = MA.pa.mb; +>checkA : 1 +>MA.pa.mb : 1 +>MA.pa : { pb: {}; mb: 1; } +>MA : typeof MA +>pa : { pb: {}; mb: 1; } +>mb : 1 + +export const checkB: 1 = MB.pb.ma; +>checkB : 1 +>MB.pb.ma : any +>MB.pb : {} +>MB : typeof MB +>pb : {} +>ma : any + +=== /ma.js === +export { v as pa } from "./xa"; +>v : { pb: {}; mb: 1; } +>pa : { pb: {}; mb: 1; } + +export const ma = 1; +>ma : 1 +>1 : 1 + +=== /mb.js === +export { v as pb } from "./xb"; +>v : {} +>pb : {} + +export const mb = 1; +>mb : 1 +>1 : 1 + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: {}; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } +>exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } + +=== /index.js === +export { v as a } from "./ia"; +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>a : { pa: { pb: {}; mb: 1; }; ma: 1; } + +export { v as b } from "./ib"; +>v : { pb: {}; mb: 1; } +>b : { pb: {}; mb: 1; } + +export const f1 = 1; +>f1 : 1 +>1 : 1 + +export const f2 = 2; +>f2 : 2 +>2 : 2 + +export const f3 = 3; +>f3 : 3 +>3 : 3 + +export const f4 = 4; +>f4 : 4 +>4 : 4 + +export const f5 = 5; +>f5 : 5 +>5 : 5 + +export const f6 = 6; +>f6 : 6 +>6 : 6 + +export const f7 = 7; +>f7 : 7 +>7 : 7 + +export const f8 = 8; +>f8 : 8 +>8 : 8 + +export const f9 = 9; +>f9 : 9 +>9 : 9 + +export const f10 = 10; +>f10 : 10 +>10 : 10 + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt new file mode 100644 index 0000000000000..408176b1c59d1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt @@ -0,0 +1,77 @@ +/main.ts(8,32): error TS2339: Property 'ma' does not exist on type '{}'. + + +==== /main.ts (1 errors) ==== + import * as idx from "./d"; + const all = { ...idx }; + import * as MA from "./ma"; + import * as MB from "./mb"; + export const fromA = MA.pa; + export const fromB = MB.pb; + export const checkA: 1 = MA.pa.mb; + export const checkB: 1 = MB.pb.ma; + ~~ +!!! error TS2339: Property 'ma' does not exist on type '{}'. + +==== /ma.js (0 errors) ==== + export { v as pa } from "./xa"; + export const ma = 1; + +==== /mb.js (0 errors) ==== + export { v as pb } from "./xb"; + export const mb = 1; + +==== /xa.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /xb.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ia.js (0 errors) ==== + /** @type {typeof import("./ma")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ib.js (0 errors) ==== + /** @type {typeof import("./mb")} */ + const M = /** @type {any} */ (null); + const o = { v: { ...M } }; + module.exports = o; + +==== /ia2.d.ts (0 errors) ==== + export { v } from "./ia"; + export type v = number; + +==== /ib2.d.ts (0 errors) ==== + export { v } from "./ib"; + export type v = number; + +==== /index.d.ts (0 errors) ==== + export { v as a } from "./ia2"; + export { v as b } from "./ib2"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + +==== /index2.d.ts (0 errors) ==== + export { v as a } from "./ia2"; + export { v as b } from "./ib2"; + +==== /d.d.ts (0 errors) ==== + export * from "./index"; + export * from "./index2"; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js new file mode 100644 index 0000000000000..beb6f57cf711b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js @@ -0,0 +1,206 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts] //// + +//// [ma.js] +export { v as pa } from "./xa"; +export const ma = 1; + +//// [mb.js] +export { v as pb } from "./xb"; +export const mb = 1; + +//// [xa.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [xb.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ia.js] +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ib.js] +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +//// [ia2.d.ts] +export { v } from "./ia"; +export type v = number; + +//// [ib2.d.ts] +export { v } from "./ib"; +export type v = number; + +//// [index.d.ts] +export { v as a } from "./ia2"; +export { v as b } from "./ib2"; +export const f1: 1; +export const f2: 2; +export const f3: 3; +export const f4: 4; +export const f5: 5; +export const f6: 6; +export const f7: 7; +export const f8: 8; +export const f9: 9; +export const f10: 10; + +//// [index2.d.ts] +export { v as a } from "./ia2"; +export { v as b } from "./ib2"; + +//// [d.d.ts] +export * from "./index"; +export * from "./index2"; + +//// [main.ts] +import * as idx from "./d"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; + + +//// [xb.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [mb.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mb = exports.pb = void 0; +var xb_1 = require("./xb"); +Object.defineProperty(exports, "pb", { enumerable: true, get: function () { return xb_1.v; } }); +exports.mb = 1; +//// [xa.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ma.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ma = exports.pa = void 0; +var xa_1 = require("./xa"); +Object.defineProperty(exports, "pa", { enumerable: true, get: function () { return xa_1.v; } }); +exports.ma = 1; +//// [ia.js] +"use strict"; +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [ib.js] +"use strict"; +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; +//// [main.js] +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkB = exports.checkA = exports.fromB = exports.fromA = void 0; +const idx = __importStar(require("./d")); +const all = { ...idx }; +const MA = __importStar(require("./ma")); +const MB = __importStar(require("./mb")); +exports.fromA = MA.pa; +exports.fromB = MB.pb; +exports.checkA = MA.pa.mb; +exports.checkB = MB.pb.ma; + + +//// [xb.d.ts] +export = o; +declare const o: { + v: {}; +}; +//// [mb.d.ts] +export { v as pb } from "./xb"; +export declare const mb = 1; +//// [xa.d.ts] +export = o; +declare const o: { + v: { + pb: {}; + mb: 1; + }; +}; +//// [ma.d.ts] +export { v as pa } from "./xa"; +export declare const ma = 1; +//// [ia.d.ts] +export = o; +declare const o: { + v: { + pa: { + pb: {}; + mb: 1; + }; + ma: 1; + }; +}; +//// [ib.d.ts] +export = o; +declare const o: { + v: { + pb: {}; + mb: 1; + }; +}; +//// [main.d.ts] +export declare const fromA: { + pb: {}; + mb: 1; +}; +export declare const fromB: {}; +export declare const checkA: 1; +export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols new file mode 100644 index 0000000000000..e737ff2cbfb8a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols @@ -0,0 +1,189 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts] //// + +=== /main.ts === +import * as idx from "./d"; +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +const all = { ...idx }; +>all : Symbol(all, Decl(main.ts, 1, 5)) +>idx : Symbol(idx, Decl(main.ts, 0, 6)) + +import * as MA from "./ma"; +>MA : Symbol(MA, Decl(main.ts, 2, 6)) + +import * as MB from "./mb"; +>MB : Symbol(MB, Decl(main.ts, 3, 6)) + +export const fromA = MA.pa; +>fromA : Symbol(fromA, Decl(main.ts, 4, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) + +export const fromB = MB.pb; +>fromB : Symbol(fromB, Decl(main.ts, 5, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) + +export const checkA: 1 = MA.pa.mb; +>checkA : Symbol(checkA, Decl(main.ts, 6, 12)) +>MA.pa.mb : Symbol(mb, Decl(mb.js, 1, 12)) +>MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>MA : Symbol(MA, Decl(main.ts, 2, 6)) +>pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>mb : Symbol(mb, Decl(mb.js, 1, 12)) + +export const checkB: 1 = MB.pb.ma; +>checkB : Symbol(checkB, Decl(main.ts, 7, 12)) +>MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>MB : Symbol(MB, Decl(main.ts, 3, 6)) +>pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) + +=== /ma.js === +export { v as pa } from "./xa"; +>v : Symbol(v, Decl(xa.js, 2, 11)) +>pa : Symbol(pa, Decl(ma.js, 0, 8)) + +export const ma = 1; +>ma : Symbol(ma, Decl(ma.js, 1, 12)) + +=== /mb.js === +export { v as pb } from "./xb"; +>v : Symbol(v, Decl(xb.js, 2, 11)) +>pb : Symbol(pb, Decl(mb.js, 0, 8)) + +export const mb = 1; +>mb : Symbol(mb, Decl(mb.js, 1, 12)) + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xa.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xa.js, 2, 5)) +>v : Symbol(v, Decl(xa.js, 2, 11)) +>M : Symbol(M, Decl(xa.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xa.js, 0, 0)) +>module : Symbol(module, Decl(xa.js, 0, 0)) +>exports : Symbol(exports, Decl(xa.js, 0, 0)) +>o : Symbol(o, Decl(xa.js, 2, 5)) + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(xb.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(xb.js, 2, 5)) +>v : Symbol(v, Decl(xb.js, 2, 11)) +>M : Symbol(M, Decl(xb.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(xb.js, 0, 0)) +>module : Symbol(module, Decl(xb.js, 0, 0)) +>exports : Symbol(exports, Decl(xb.js, 0, 0)) +>o : Symbol(o, Decl(xb.js, 2, 5)) + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ia.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ia.js, 2, 5)) +>v : Symbol(v, Decl(ia.js, 2, 11)) +>M : Symbol(M, Decl(ia.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ia.js, 0, 0)) +>module : Symbol(module, Decl(ia.js, 0, 0)) +>exports : Symbol(exports, Decl(ia.js, 0, 0)) +>o : Symbol(o, Decl(ia.js, 2, 5)) + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : Symbol(M, Decl(ib.js, 1, 5)) + +const o = { v: { ...M } }; +>o : Symbol(o, Decl(ib.js, 2, 5)) +>v : Symbol(v, Decl(ib.js, 2, 11)) +>M : Symbol(M, Decl(ib.js, 1, 5)) + +module.exports = o; +>module.exports : Symbol(exports, Decl(ib.js, 0, 0)) +>module : Symbol(module, Decl(ib.js, 0, 0)) +>exports : Symbol(exports, Decl(ib.js, 0, 0)) +>o : Symbol(o, Decl(ib.js, 2, 5)) + +=== /ia2.d.ts === +export { v } from "./ia"; +>v : Symbol(v, Decl(ia2.d.ts, 0, 8), Decl(ia2.d.ts, 0, 25)) + +export type v = number; +>v : Symbol(v, Decl(ia2.d.ts, 0, 8), Decl(ia2.d.ts, 0, 25)) + +=== /ib2.d.ts === +export { v } from "./ib"; +>v : Symbol(v, Decl(ib2.d.ts, 0, 8), Decl(ib2.d.ts, 0, 25)) + +export type v = number; +>v : Symbol(v, Decl(ib2.d.ts, 0, 8), Decl(ib2.d.ts, 0, 25)) + +=== /index.d.ts === +export { v as a } from "./ia2"; +>v : Symbol(v, Decl(ia2.d.ts, 0, 8), Decl(ia2.d.ts, 0, 25)) +>a : Symbol(a, Decl(index.d.ts, 0, 8)) + +export { v as b } from "./ib2"; +>v : Symbol(v, Decl(ib2.d.ts, 0, 8), Decl(ib2.d.ts, 0, 25)) +>b : Symbol(b, Decl(index.d.ts, 1, 8)) + +export const f1: 1; +>f1 : Symbol(f1, Decl(index.d.ts, 2, 12)) + +export const f2: 2; +>f2 : Symbol(f2, Decl(index.d.ts, 3, 12)) + +export const f3: 3; +>f3 : Symbol(f3, Decl(index.d.ts, 4, 12)) + +export const f4: 4; +>f4 : Symbol(f4, Decl(index.d.ts, 5, 12)) + +export const f5: 5; +>f5 : Symbol(f5, Decl(index.d.ts, 6, 12)) + +export const f6: 6; +>f6 : Symbol(f6, Decl(index.d.ts, 7, 12)) + +export const f7: 7; +>f7 : Symbol(f7, Decl(index.d.ts, 8, 12)) + +export const f8: 8; +>f8 : Symbol(f8, Decl(index.d.ts, 9, 12)) + +export const f9: 9; +>f9 : Symbol(f9, Decl(index.d.ts, 10, 12)) + +export const f10: 10; +>f10 : Symbol(f10, Decl(index.d.ts, 11, 12)) + +=== /index2.d.ts === +export { v as a } from "./ia2"; +>v : Symbol(v, Decl(ia2.d.ts, 0, 8), Decl(ia2.d.ts, 0, 25)) +>a : Symbol(a, Decl(index2.d.ts, 0, 8)) + +export { v as b } from "./ib2"; +>v : Symbol(v, Decl(ib2.d.ts, 0, 8), Decl(ib2.d.ts, 0, 25)) +>b : Symbol(b, Decl(index2.d.ts, 1, 8)) + +=== /d.d.ts === + +export * from "./index"; +export * from "./index2"; + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types new file mode 100644 index 0000000000000..acecbd2516c82 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types @@ -0,0 +1,210 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts] //// + +=== /main.ts === +import * as idx from "./d"; +>idx : typeof idx + +const all = { ...idx }; +>all : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>idx : typeof idx + +import * as MA from "./ma"; +>MA : typeof MA + +import * as MB from "./mb"; +>MB : typeof MB + +export const fromA = MA.pa; +>fromA : { pb: {}; mb: 1; } +>MA.pa : { pb: {}; mb: 1; } +>MA : typeof MA +>pa : { pb: {}; mb: 1; } + +export const fromB = MB.pb; +>fromB : {} +>MB.pb : {} +>MB : typeof MB +>pb : {} + +export const checkA: 1 = MA.pa.mb; +>checkA : 1 +>MA.pa.mb : 1 +>MA.pa : { pb: {}; mb: 1; } +>MA : typeof MA +>pa : { pb: {}; mb: 1; } +>mb : 1 + +export const checkB: 1 = MB.pb.ma; +>checkB : 1 +>MB.pb.ma : any +>MB.pb : {} +>MB : typeof MB +>pb : {} +>ma : any + +=== /ma.js === +export { v as pa } from "./xa"; +>v : { pb: {}; mb: 1; } +>pa : { pb: {}; mb: 1; } + +export const ma = 1; +>ma : 1 +>1 : 1 + +=== /mb.js === +export { v as pb } from "./xb"; +>v : {} +>pb : {} + +export const mb = 1; +>mb : 1 +>1 : 1 + +=== /xa.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } + +=== /xb.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: {}; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } + +=== /ia.js === +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./ma") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>M : typeof import("./ma") + +module.exports = o; +>module.exports = o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } +>exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } + +=== /ib.js === +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +>M : typeof import("./mb") +>(null) : any + +const o = { v: { ...M } }; +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } +>M : typeof import("./mb") + +module.exports = o; +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } + +=== /ia2.d.ts === +export { v } from "./ia"; +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } + +export type v = number; +>v : number + +=== /ib2.d.ts === +export { v } from "./ib"; +>v : { pb: {}; mb: 1; } + +export type v = number; +>v : number + +=== /index.d.ts === +export { v as a } from "./ia2"; +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>a : { pa: { pb: {}; mb: 1; }; ma: 1; } + +export { v as b } from "./ib2"; +>v : { pb: {}; mb: 1; } +>b : { pb: {}; mb: 1; } + +export const f1: 1; +>f1 : 1 + +export const f2: 2; +>f2 : 2 + +export const f3: 3; +>f3 : 3 + +export const f4: 4; +>f4 : 4 + +export const f5: 5; +>f5 : 5 + +export const f6: 6; +>f6 : 6 + +export const f7: 7; +>f7 : 7 + +export const f8: 8; +>f8 : 8 + +export const f9: 9; +>f9 : 9 + +export const f10: 10; +>f10 : 10 + +=== /index2.d.ts === +export { v as a } from "./ia2"; +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>a : { pa: { pb: {}; mb: 1; }; ma: 1; } + +export { v as b } from "./ib2"; +>v : { pb: {}; mb: 1; } +>b : { pb: {}; mb: 1; } + +=== /d.d.ts === + +export * from "./index"; +export * from "./index2"; + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt new file mode 100644 index 0000000000000..aba9bd814f87b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt @@ -0,0 +1,70 @@ +/main.ts(2,10): error TS2459: Module '"mt"' declares 'zz' locally, but it is not exported. +/main.ts(6,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. +/main.ts(7,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + + +==== /main.ts (3 errors) ==== + /// + import { zz } from "mt"; + ~~ +!!! error TS2459: Module '"mt"' declares 'zz' locally, but it is not exported. +!!! related TS2728 /ambient.d.ts:46:11: 'zz' is declared here. + declare const oxa: typeof import("xa"); + declare const oxb: typeof import("xb"); + declare function expand(): { [K in keyof X]: X[K] }; + export const obsA: never = expand(); + ~~~~ +!!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. + export const obsB: never = expand(); + ~~~~ +!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + +==== /ambient.d.ts (0 errors) ==== + declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; + } + declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; + } + declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "mt" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + const zz: 1; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.symbols new file mode 100644 index 0000000000000..c5f4fec719b8c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.symbols @@ -0,0 +1,169 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNonExportedMemberOrder.ts] //// + +=== /main.ts === +/// +import { zz } from "mt"; +>zz : Symbol(zz, Decl(main.ts, 1, 8)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) + +export const obsA: never = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 5, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +export const obsB: never = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "mt" { +>"mt" : Symbol("mt", Decl(ambient.d.ts, 31, 1)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 33, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 34, 12)) + + export const f1: 1; +>f1 : Symbol(f1, Decl(ambient.d.ts, 35, 16)) + + export const f2: 2; +>f2 : Symbol(f2, Decl(ambient.d.ts, 36, 16)) + + export const f3: 3; +>f3 : Symbol(f3, Decl(ambient.d.ts, 37, 16)) + + export const f4: 4; +>f4 : Symbol(f4, Decl(ambient.d.ts, 38, 16)) + + export const f5: 5; +>f5 : Symbol(f5, Decl(ambient.d.ts, 39, 16)) + + export const f6: 6; +>f6 : Symbol(f6, Decl(ambient.d.ts, 40, 16)) + + export const f7: 7; +>f7 : Symbol(f7, Decl(ambient.d.ts, 41, 16)) + + export const f8: 8; +>f8 : Symbol(f8, Decl(ambient.d.ts, 42, 16)) + + export const f9: 9; +>f9 : Symbol(f9, Decl(ambient.d.ts, 43, 16)) + + export const f10: 10; +>f10 : Symbol(f10, Decl(ambient.d.ts, 44, 16)) + + const zz: 1; +>zz : Symbol(zz, Decl(ambient.d.ts, 45, 9)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types new file mode 100644 index 0000000000000..00ba3854da277 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types @@ -0,0 +1,154 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsNonExportedMemberOrder.ts] //// + +=== /main.ts === +/// +import { zz } from "mt"; +>zz : any + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const obsA: never = expand(); +>obsA : never +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB: never = expand(); +>obsB : never +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "mt" { +>"mt" : typeof import("mt") + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export const f1: 1; +>f1 : 1 + + export const f2: 2; +>f2 : 2 + + export const f3: 3; +>f3 : 3 + + export const f4: 4; +>f4 : 4 + + export const f5: 5; +>f5 : 5 + + export const f6: 6; +>f6 : 6 + + export const f7: 7; +>f7 : 7 + + export const f8: 8; +>f8 : 8 + + export const f9: 9; +>f9 : 9 + + export const f10: 10; +>f10 : 10 + + const zz: 1; +>zz : 1 +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.errors.txt new file mode 100644 index 0000000000000..d8667784e78ea --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.errors.txt @@ -0,0 +1,126 @@ +/main.ts(17,11): error TS2304: Cannot find name 'zz'. +/main.ts(18,7): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. +/main.ts(19,7): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + + +==== /main.ts (3 errors) ==== + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + declare const oxa: typeof import("xa"); + declare const oxb: typeof import("xb"); + declare function expand(): { [K in keyof X]: X[K] }; + const q = zz; + ~~ +!!! error TS2304: Cannot find name 'zz'. + const obsA: never = expand(); + ~~~~ +!!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. + const obsB: never = expand(); + ~~~~ +!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. + +==== /ambient.d.ts (0 errors) ==== + declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; + } + declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; + } + declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + +==== /ua.d.ts (0 errors) ==== + import { v } from "ia"; + export = v; + export as namespace UA; + +==== /ub.d.ts (0 errors) ==== + import { v } from "ib"; + export = v; + export as namespace UB; + +==== /uf1.d.ts (0 errors) ==== + declare const x: 1; + export = x; + export as namespace UF1; + +==== /uf2.d.ts (0 errors) ==== + declare const x: 2; + export = x; + export as namespace UF2; + +==== /uf3.d.ts (0 errors) ==== + declare const x: 3; + export = x; + export as namespace UF3; + +==== /uf4.d.ts (0 errors) ==== + declare const x: 4; + export = x; + export as namespace UF4; + +==== /uf5.d.ts (0 errors) ==== + declare const x: 5; + export = x; + export as namespace UF5; + +==== /uf6.d.ts (0 errors) ==== + declare const x: 6; + export = x; + export as namespace UF6; + +==== /uf7.d.ts (0 errors) ==== + declare const x: 7; + export = x; + export as namespace UF7; + +==== /uf8.d.ts (0 errors) ==== + declare const x: 8; + export = x; + export as namespace UF8; + +==== /uf9.d.ts (0 errors) ==== + declare const x: 9; + export = x; + export as namespace UF9; + +==== /uf10.d.ts (0 errors) ==== + declare const x: 10; + export = x; + export as namespace UF10; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.symbols new file mode 100644 index 0000000000000..c855f73560d36 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.symbols @@ -0,0 +1,257 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.ts] //// + +=== /main.ts === +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 13, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 14, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>K : Symbol(K, Decl(main.ts, 15, 33)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>X : Symbol(X, Decl(main.ts, 15, 24)) +>K : Symbol(K, Decl(main.ts, 15, 33)) + +const q = zz; +>q : Symbol(q, Decl(main.ts, 16, 5)) + +const obsA: never = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 17, 5)) +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 13, 13)) + +const obsB: never = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 18, 5)) +>expand : Symbol(expand, Decl(main.ts, 14, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 14, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(UA, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(UB, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + +=== /ua.d.ts === +import { v } from "ia"; +>v : Symbol(v, Decl(ua.d.ts, 0, 8)) + +export = v; +>v : Symbol(v, Decl(ua.d.ts, 0, 8)) + +export as namespace UA; +>UA : Symbol(UA, Decl(ua.d.ts, 1, 11)) + +=== /ub.d.ts === +import { v } from "ib"; +>v : Symbol(v, Decl(ub.d.ts, 0, 8)) + +export = v; +>v : Symbol(v, Decl(ub.d.ts, 0, 8)) + +export as namespace UB; +>UB : Symbol(UB, Decl(ub.d.ts, 1, 11)) + +=== /uf1.d.ts === +declare const x: 1; +>x : Symbol(x, Decl(uf1.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf1.d.ts, 0, 13)) + +export as namespace UF1; +>UF1 : Symbol(UF1, Decl(uf1.d.ts, 1, 11)) + +=== /uf2.d.ts === +declare const x: 2; +>x : Symbol(x, Decl(uf2.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf2.d.ts, 0, 13)) + +export as namespace UF2; +>UF2 : Symbol(UF2, Decl(uf2.d.ts, 1, 11)) + +=== /uf3.d.ts === +declare const x: 3; +>x : Symbol(x, Decl(uf3.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf3.d.ts, 0, 13)) + +export as namespace UF3; +>UF3 : Symbol(UF3, Decl(uf3.d.ts, 1, 11)) + +=== /uf4.d.ts === +declare const x: 4; +>x : Symbol(x, Decl(uf4.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf4.d.ts, 0, 13)) + +export as namespace UF4; +>UF4 : Symbol(UF4, Decl(uf4.d.ts, 1, 11)) + +=== /uf5.d.ts === +declare const x: 5; +>x : Symbol(x, Decl(uf5.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf5.d.ts, 0, 13)) + +export as namespace UF5; +>UF5 : Symbol(UF5, Decl(uf5.d.ts, 1, 11)) + +=== /uf6.d.ts === +declare const x: 6; +>x : Symbol(x, Decl(uf6.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf6.d.ts, 0, 13)) + +export as namespace UF6; +>UF6 : Symbol(UF6, Decl(uf6.d.ts, 1, 11)) + +=== /uf7.d.ts === +declare const x: 7; +>x : Symbol(x, Decl(uf7.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf7.d.ts, 0, 13)) + +export as namespace UF7; +>UF7 : Symbol(UF7, Decl(uf7.d.ts, 1, 11)) + +=== /uf8.d.ts === +declare const x: 8; +>x : Symbol(x, Decl(uf8.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf8.d.ts, 0, 13)) + +export as namespace UF8; +>UF8 : Symbol(UF8, Decl(uf8.d.ts, 1, 11)) + +=== /uf9.d.ts === +declare const x: 9; +>x : Symbol(x, Decl(uf9.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf9.d.ts, 0, 13)) + +export as namespace UF9; +>UF9 : Symbol(UF9, Decl(uf9.d.ts, 1, 11)) + +=== /uf10.d.ts === +declare const x: 10; +>x : Symbol(x, Decl(uf10.d.ts, 0, 13)) + +export = x; +>x : Symbol(x, Decl(uf10.d.ts, 0, 13)) + +export as namespace UF10; +>UF10 : Symbol(UF10, Decl(uf10.d.ts, 1, 11)) + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.types new file mode 100644 index 0000000000000..0c8a7c9521634 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.types @@ -0,0 +1,243 @@ +//// [tests/cases/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.ts] //// + +=== /main.ts === +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +const q = zz; +>q : any +>zz : any + +const obsA: never = expand(); +>obsA : never +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +const obsB: never = expand(); +>obsB : never +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + +=== /ua.d.ts === +import { v } from "ia"; +>v : 0 + +export = v; +>v : 0 + +export as namespace UA; +>UA : 0 + +=== /ub.d.ts === +import { v } from "ib"; +>v : 0 + +export = v; +>v : 0 + +export as namespace UB; +>UB : 0 + +=== /uf1.d.ts === +declare const x: 1; +>x : 1 + +export = x; +>x : 1 + +export as namespace UF1; +>UF1 : 1 + +=== /uf2.d.ts === +declare const x: 2; +>x : 2 + +export = x; +>x : 2 + +export as namespace UF2; +>UF2 : 2 + +=== /uf3.d.ts === +declare const x: 3; +>x : 3 + +export = x; +>x : 3 + +export as namespace UF3; +>UF3 : 3 + +=== /uf4.d.ts === +declare const x: 4; +>x : 4 + +export = x; +>x : 4 + +export as namespace UF4; +>UF4 : 4 + +=== /uf5.d.ts === +declare const x: 5; +>x : 5 + +export = x; +>x : 5 + +export as namespace UF5; +>UF5 : 5 + +=== /uf6.d.ts === +declare const x: 6; +>x : 6 + +export = x; +>x : 6 + +export as namespace UF6; +>UF6 : 6 + +=== /uf7.d.ts === +declare const x: 7; +>x : 7 + +export = x; +>x : 7 + +export as namespace UF7; +>UF7 : 7 + +=== /uf8.d.ts === +declare const x: 8; +>x : 8 + +export = x; +>x : 8 + +export as namespace UF8; +>UF8 : 8 + +=== /uf9.d.ts === +declare const x: 9; +>x : 9 + +export = x; +>x : 9 + +export as namespace UF9; +>UF9 : 9 + +=== /uf10.d.ts === +declare const x: 10; +>x : 10 + +export = x; +>x : 10 + +export as namespace UF10; +>UF10 : 10 + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt new file mode 100644 index 0000000000000..d81eb346f2055 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt @@ -0,0 +1,63 @@ +/mm.ts(5,1): error TS2309: An export assignment cannot be used in a module with other exported elements. +/mm.ts(19,30): error TS2339: Property 'pa' does not exist on type 'O'. + + +==== /mm.ts (2 errors) ==== + /// + declare const oxa: typeof import("xa"); + declare const oxb: typeof import("xb"); + declare const o: { x: 1 }; + export = o; + ~~~~~~~~~~~ +!!! error TS2309: An export assignment cannot be used in a module with other exported elements. + export { v as a } from "ia"; + export { v as b } from "ib"; + export interface F1 {} + export interface F2 {} + export interface F3 {} + export interface F4 {} + export interface F5 {} + export interface F6 {} + export interface F7 {} + export interface F8 {} + export interface F9 {} + export interface F10 {} + export const checkA: 0 = oxa.pb; + export const checkB: 0 = oxb.pa; + ~~ +!!! error TS2339: Property 'pa' does not exist on type 'O'. + +==== /ambient.d.ts (0 errors) ==== + declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; + } + declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; + } + declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols new file mode 100644 index 0000000000000..74a9fa86901eb --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols @@ -0,0 +1,159 @@ +//// [tests/cases/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.ts] //// + +=== /mm.ts === +/// +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(mm.ts, 1, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(mm.ts, 2, 13)) + +declare const o: { x: 1 }; +>o : Symbol(o, Decl(mm.ts, 3, 13)) +>x : Symbol(x, Decl(mm.ts, 3, 18)) + +export = o; +>o : Symbol(o, Decl(mm.ts, 3, 13)) + +export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(mm.ts, 5, 8)) + +export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(mm.ts, 6, 8)) + +export interface F1 {} +>F1 : Symbol(F1, Decl(mm.ts, 6, 28)) + +export interface F2 {} +>F2 : Symbol(F2, Decl(mm.ts, 7, 22)) + +export interface F3 {} +>F3 : Symbol(F3, Decl(mm.ts, 8, 22)) + +export interface F4 {} +>F4 : Symbol(F4, Decl(mm.ts, 9, 22)) + +export interface F5 {} +>F5 : Symbol(F5, Decl(mm.ts, 10, 22)) + +export interface F6 {} +>F6 : Symbol(F6, Decl(mm.ts, 11, 22)) + +export interface F7 {} +>F7 : Symbol(F7, Decl(mm.ts, 12, 22)) + +export interface F8 {} +>F8 : Symbol(F8, Decl(mm.ts, 13, 22)) + +export interface F9 {} +>F9 : Symbol(F9, Decl(mm.ts, 14, 22)) + +export interface F10 {} +>F10 : Symbol(F10, Decl(mm.ts, 15, 22)) + +export const checkA: 0 = oxa.pb; +>checkA : Symbol(checkA, Decl(mm.ts, 17, 12)) +>oxa.pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) +>oxa : Symbol(oxa, Decl(mm.ts, 1, 13)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + +export const checkB: 0 = oxb.pa; +>checkB : Symbol(checkB, Decl(mm.ts, 18, 12)) +>oxb : Symbol(oxb, Decl(mm.ts, 2, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types new file mode 100644 index 0000000000000..44664dcadfb80 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types @@ -0,0 +1,129 @@ +//// [tests/cases/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.ts] //// + +=== /mm.ts === +/// +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare const o: { x: 1 }; +>o : { x: 1; } +>x : 1 + +export = o; +>o : { x: 1; } + +export { v as a } from "ia"; +>v : 0 +>a : 0 + +export { v as b } from "ib"; +>v : 0 +>b : 0 + +export interface F1 {} +export interface F2 {} +export interface F3 {} +export interface F4 {} +export interface F5 {} +export interface F6 {} +export interface F7 {} +export interface F8 {} +export interface F9 {} +export interface F10 {} +export const checkA: 0 = oxa.pb; +>checkA : 0 +>oxa.pb : 0 +>oxa : O +>pb : 0 + +export const checkB: 0 = oxb.pa; +>checkB : 0 +>oxb.pa : any +>oxb : O +>pa : any + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js new file mode 100644 index 0000000000000..37e7a9ff94890 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts] //// + +//// [ambient.d.ts] +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mm" { + const o: { x: 1 }; + export = o; + export { v as a } from "ia"; + export { v as b } from "ib"; + export interface F1 {} + export interface F2 {} + export interface F3 {} + export interface F4 {} + export interface F5 {} + export interface F6 {} + export interface F7 {} + export interface F8 {} + export interface F9 {} + export interface F10 {} +} + +//// [main.ts] +/// +import { x } from "mm"; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = x; +export const obsA = expand(); +export const obsB = expand(); + + + + +//// [main.d.ts] +export declare const keep: 1; +export declare const obsA: { + pb: 0; + readonly mb: 1; + v: 0; +}; +export declare const obsB: { + v: 0; +}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.symbols new file mode 100644 index 0000000000000..0e5471b217c97 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.symbols @@ -0,0 +1,177 @@ +//// [tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts] //// + +=== /main.ts === +/// +import { x } from "mm"; +>x : Symbol(x, Decl(main.ts, 1, 8)) + +declare const oxa: typeof import("xa"); +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +declare const oxb: typeof import("xb"); +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>X : Symbol(X, Decl(main.ts, 4, 24)) +>K : Symbol(K, Decl(main.ts, 4, 33)) + +export const keep = x; +>keep : Symbol(keep, Decl(main.ts, 5, 12)) +>x : Symbol(x, Decl(main.ts, 1, 8)) + +export const obsA = expand(); +>obsA : Symbol(obsA, Decl(main.ts, 6, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxa : Symbol(oxa, Decl(main.ts, 2, 13)) + +export const obsB = expand(); +>obsB : Symbol(obsB, Decl(main.ts, 7, 12)) +>expand : Symbol(expand, Decl(main.ts, 3, 39)) +>oxb : Symbol(oxb, Decl(main.ts, 3, 13)) + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : Symbol("ma", Decl(ambient.d.ts, 0, 0)) + + export { v as pa } from "xa"; +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) + + export const ma: 1; +>ma : Symbol(ma, Decl(ambient.d.ts, 2, 16)) +} +declare module "mb" { +>"mb" : Symbol("mb", Decl(ambient.d.ts, 3, 1)) + + export { v as pb } from "xb"; +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) +>pb : Symbol(pb, Decl(ambient.d.ts, 5, 12)) + + export const mb: 1; +>mb : Symbol(mb, Decl(ambient.d.ts, 6, 16)) +} +declare module "xa" { +>"xa" : Symbol("xa", Decl(ambient.d.ts, 7, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 8, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 10, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 9, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 11, 9)) +} +declare module "xb" { +>"xb" : Symbol("xb", Decl(ambient.d.ts, 13, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 14, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 16, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 15, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 17, 9)) +} +declare module "ia" { +>"ia" : Symbol("ia", Decl(ambient.d.ts, 19, 1)) + + type T = typeof import("ma"); +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 20, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 21, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 23, 9)) +} +declare module "ib" { +>"ib" : Symbol("ib", Decl(ambient.d.ts, 25, 1)) + + type T = typeof import("mb"); +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) + + interface O extends T { v: 0 } +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) +>T : Symbol(T, Decl(ambient.d.ts, 26, 21)) +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) + + const o: O; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +>O : Symbol(O, Decl(ambient.d.ts, 27, 33)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 29, 9)) +} +declare module "mm" { +>"mm" : Symbol("mm", Decl(ambient.d.ts, 31, 1)) + + const o: { x: 1 }; +>o : Symbol(o, Decl(ambient.d.ts, 33, 9)) +>x : Symbol(x, Decl(ambient.d.ts, 33, 14)) + + export = o; +>o : Symbol(o, Decl(ambient.d.ts, 33, 9)) + + export { v as a } from "ia"; +>v : Symbol(O.v, Decl(ambient.d.ts, 22, 27)) +>a : Symbol(a, Decl(ambient.d.ts, 35, 12)) + + export { v as b } from "ib"; +>v : Symbol(O.v, Decl(ambient.d.ts, 28, 27)) +>b : Symbol(b, Decl(ambient.d.ts, 36, 12)) + + export interface F1 {} +>F1 : Symbol(F1, Decl(ambient.d.ts, 36, 32)) + + export interface F2 {} +>F2 : Symbol(F2, Decl(ambient.d.ts, 37, 26)) + + export interface F3 {} +>F3 : Symbol(F3, Decl(ambient.d.ts, 38, 26)) + + export interface F4 {} +>F4 : Symbol(F4, Decl(ambient.d.ts, 39, 26)) + + export interface F5 {} +>F5 : Symbol(F5, Decl(ambient.d.ts, 40, 26)) + + export interface F6 {} +>F6 : Symbol(F6, Decl(ambient.d.ts, 41, 26)) + + export interface F7 {} +>F7 : Symbol(F7, Decl(ambient.d.ts, 42, 26)) + + export interface F8 {} +>F8 : Symbol(F8, Decl(ambient.d.ts, 43, 26)) + + export interface F9 {} +>F9 : Symbol(F9, Decl(ambient.d.ts, 44, 26)) + + export interface F10 {} +>F10 : Symbol(F10, Decl(ambient.d.ts, 45, 26)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types new file mode 100644 index 0000000000000..c0ba114c78355 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types @@ -0,0 +1,143 @@ +//// [tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts] //// + +=== /main.ts === +/// +import { x } from "mm"; +>x : 1 + +declare const oxa: typeof import("xa"); +>oxa : O + +declare const oxb: typeof import("xb"); +>oxb : O + +declare function expand(): { [K in keyof X]: X[K] }; +>expand : () => { [K in keyof X]: X[K]; } + +export const keep = x; +>keep : 1 +>x : 1 + +export const obsA = expand(); +>obsA : { pb: 0; readonly mb: 1; v: 0; } +>expand() : { pb: 0; readonly mb: 1; v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxa : O + +export const obsB = expand(); +>obsB : { v: 0; } +>expand() : { v: 0; } +>expand : () => { [K in keyof X]: X[K]; } +>oxb : O + +=== /ambient.d.ts === +declare module "ma" { +>"ma" : typeof import("ma") + + export { v as pa } from "xa"; +>v : 0 +>pa : 0 + + export const ma: 1; +>ma : 1 +} +declare module "mb" { +>"mb" : typeof import("mb") + + export { v as pb } from "xb"; +>v : 0 +>pb : 0 + + export const mb: 1; +>mb : 1 +} +declare module "xa" { +>"xa" : typeof import("xa") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "xb" { +>"xb" : typeof import("xb") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ia" { +>"ia" : typeof import("ia") + + type T = typeof import("ma"); +>T : typeof import("ma") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "ib" { +>"ib" : typeof import("ib") + + type T = typeof import("mb"); +>T : typeof import("mb") + + interface O extends T { v: 0 } +>v : 0 + + const o: O; +>o : O + + export = o; +>o : O +} +declare module "mm" { +>"mm" : typeof import("mm") + + const o: { x: 1 }; +>o : { x: 1; } +>x : 1 + + export = o; +>o : { x: 1; } + + export { v as a } from "ia"; +>v : 0 +>a : 0 + + export { v as b } from "ib"; +>v : 0 +>b : 0 + + export interface F1 {} + export interface F2 {} + export interface F3 {} + export interface F4 {} + export interface F5 {} + export interface F6 {} + export interface F7 {} + export interface F8 {} + export interface F9 {} + export interface F10 {} +} + diff --git a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash1.errors.txt b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash1.errors.txt index 19991af5050fb..c70c7a1066f83 100644 --- a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash1.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash1.errors.txt @@ -7,11 +7,12 @@ parametersSyntaxErrorNoCrash1.ts(3,32): error TS1005: ',' expected. parametersSyntaxErrorNoCrash1.ts(4,12): error TS1005: ':' expected. parametersSyntaxErrorNoCrash1.ts(4,12): error TS2300: Duplicate identifier 'arg'. parametersSyntaxErrorNoCrash1.ts(4,12): error TS2842: 'arg' is an unused renaming of 'return'. Did you intend to use it as a type annotation? +parametersSyntaxErrorNoCrash1.ts(4,12): error TS7031: Binding element 'arg' implicitly has an 'any' type. parametersSyntaxErrorNoCrash1.ts(4,15): error TS1005: ',' expected. parametersSyntaxErrorNoCrash1.ts(5,2): error TS1005: ')' expected. -==== parametersSyntaxErrorNoCrash1.ts (11 errors) ==== +==== parametersSyntaxErrorNoCrash1.ts (12 errors) ==== // https://github.com/microsoft/TypeScript/issues/59422 function identity(arg: T: T { @@ -35,6 +36,8 @@ parametersSyntaxErrorNoCrash1.ts(5,2): error TS1005: ')' expected. ~~~ !!! error TS2842: 'arg' is an unused renaming of 'return'. Did you intend to use it as a type annotation? !!! related TS2843 parametersSyntaxErrorNoCrash1.ts:5:2: We can only write a type for 'return' by adding a type for the entire parameter here. + ~~~ +!!! error TS7031: Binding element 'arg' implicitly has an 'any' type. ~ !!! error TS1005: ',' expected. } diff --git a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash2.errors.txt b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash2.errors.txt index 4df2a2a5dda87..da4b95b3163c0 100644 --- a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash2.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash2.errors.txt @@ -2,14 +2,16 @@ parametersSyntaxErrorNoCrash2.ts(3,25): error TS2391: Function implementation is parametersSyntaxErrorNoCrash2.ts(3,25): error TS7010: 'getThing', which lacks return-type annotation, implicitly has an 'any' return type. parametersSyntaxErrorNoCrash2.ts(3,43): error TS2300: Duplicate identifier '(Missing)'. parametersSyntaxErrorNoCrash2.ts(3,43): error TS2842: '(Missing)' is an unused renaming of 'return'. Did you intend to use it as a type annotation? +parametersSyntaxErrorNoCrash2.ts(3,43): error TS7031: Binding element '(Missing)' implicitly has an 'any' type. parametersSyntaxErrorNoCrash2.ts(3,44): error TS1005: ':' expected. parametersSyntaxErrorNoCrash2.ts(3,51): error TS2300: Duplicate identifier '(Missing)'. parametersSyntaxErrorNoCrash2.ts(3,51): error TS2842: '(Missing)' is an unused renaming of ''thing''. Did you intend to use it as a type annotation? +parametersSyntaxErrorNoCrash2.ts(3,51): error TS7031: Binding element '(Missing)' implicitly has an 'any' type. parametersSyntaxErrorNoCrash2.ts(3,51): error TS1005: ':' expected. parametersSyntaxErrorNoCrash2.ts(3,54): error TS1005: ')' expected. -==== parametersSyntaxErrorNoCrash2.ts (9 errors) ==== +==== parametersSyntaxErrorNoCrash2.ts (11 errors) ==== // https://github.com/microsoft/TypeScript/issues/59353 export default function getThing( { return 'thing'; } @@ -22,6 +24,8 @@ parametersSyntaxErrorNoCrash2.ts(3,54): error TS1005: ')' expected. !!! error TS2842: '(Missing)' is an unused renaming of 'return'. Did you intend to use it as a type annotation? !!! related TS2843 parametersSyntaxErrorNoCrash2.ts:3:54: We can only write a type for 'return' by adding a type for the entire parameter here. + +!!! error TS7031: Binding element '(Missing)' implicitly has an 'any' type. ~~~~~~~ !!! error TS1005: ':' expected. @@ -29,6 +33,8 @@ parametersSyntaxErrorNoCrash2.ts(3,54): error TS1005: ')' expected. !!! error TS2842: '(Missing)' is an unused renaming of ''thing''. Did you intend to use it as a type annotation? !!! related TS2843 parametersSyntaxErrorNoCrash2.ts:3:54: We can only write a type for ''thing'' by adding a type for the entire parameter here. + +!!! error TS7031: Binding element '(Missing)' implicitly has an 'any' type. ~ !!! error TS1005: ':' expected. diff --git a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash3.errors.txt b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash3.errors.txt index 7af7ce2b628ce..863b10603b210 100644 --- a/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash3.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/parametersSyntaxErrorNoCrash3.errors.txt @@ -2,14 +2,16 @@ parametersSyntaxErrorNoCrash3.ts(3,17): error TS2391: Function implementation is parametersSyntaxErrorNoCrash3.ts(3,17): error TS7010: 'getHtml', which lacks return-type annotation, implicitly has an 'any' return type. parametersSyntaxErrorNoCrash3.ts(4,11): error TS2300: Duplicate identifier '(Missing)'. parametersSyntaxErrorNoCrash3.ts(4,11): error TS2842: '(Missing)' is an unused renaming of 'return'. Did you intend to use it as a type annotation? +parametersSyntaxErrorNoCrash3.ts(4,11): error TS7031: Binding element '(Missing)' implicitly has an 'any' type. parametersSyntaxErrorNoCrash3.ts(4,13): error TS1005: ':' expected. parametersSyntaxErrorNoCrash3.ts(4,22): error TS2300: Duplicate identifier '(Missing)'. parametersSyntaxErrorNoCrash3.ts(4,22): error TS2842: '(Missing)' is an unused renaming of '" string"'. Did you intend to use it as a type annotation? +parametersSyntaxErrorNoCrash3.ts(4,22): error TS7031: Binding element '(Missing)' implicitly has an 'any' type. parametersSyntaxErrorNoCrash3.ts(5,1): error TS1005: ':' expected. parametersSyntaxErrorNoCrash3.ts(5,2): error TS1005: ')' expected. -==== parametersSyntaxErrorNoCrash3.ts (9 errors) ==== +==== parametersSyntaxErrorNoCrash3.ts (11 errors) ==== // https://github.com/microsoft/TypeScript/issues/59449 export function getHtml({ @@ -23,6 +25,8 @@ parametersSyntaxErrorNoCrash3.ts(5,2): error TS1005: ')' expected. !!! error TS2842: '(Missing)' is an unused renaming of 'return'. Did you intend to use it as a type annotation? !!! related TS2843 parametersSyntaxErrorNoCrash3.ts:5:2: We can only write a type for 'return' by adding a type for the entire parameter here. + +!!! error TS7031: Binding element '(Missing)' implicitly has an 'any' type. ~~~~~~~~~ !!! error TS1005: ':' expected. @@ -30,6 +34,8 @@ parametersSyntaxErrorNoCrash3.ts(5,2): error TS1005: ')' expected. !!! error TS2842: '(Missing)' is an unused renaming of '" string"'. Did you intend to use it as a type annotation? !!! related TS2843 parametersSyntaxErrorNoCrash3.ts:5:2: We can only write a type for '" string"' by adding a type for the entire parameter here. + +!!! error TS7031: Binding element '(Missing)' implicitly has an 'any' type. } ~ !!! error TS1005: ':' expected. diff --git a/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedEmptyModule.errors.txt b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedEmptyModule.errors.txt new file mode 100644 index 0000000000000..f5c57582a5fbf --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedEmptyModule.errors.txt @@ -0,0 +1,61 @@ +/src/main.ts(4,8): error TS1192: Module '"/src/script"' has no default export. +/src/main.ts(5,7): error TS2322: Type 'typeof import("/stub")' is not assignable to type 'number'. +/src/main.ts(6,7): error TS2322: Type 'typeof import("/stub")' is not assignable to type 'number'. +/src/main.ts(7,7): error TS2322: Type 'typeof import("/generated/component.svelte")' is not assignable to type 'number'. + + +==== /tsconfig.json (0 errors) ==== + { + "compilerOptions": { + "paths": { "x.svelte": ["./stub.d.ts"] }, + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] + } + +==== /ambient.d.ts (0 errors) ==== + declare module "*.svelte" { + const value: number; + export default value; + } + declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; + } + declare module "*.js" { + const value: number; + export default value; + } + +==== /src/main.ts (4 errors) ==== + import fromPaths from "x.svelte"; + import emptyAttributes from "x.svelte" with {}; + import fromRootDirs from "./component.svelte"; + import fromRelative from "./script.js"; + ~~~~~~~~~~~~ +!!! error TS1192: Module '"/src/script"' has no default export. + const fromPathsValue: number = fromPaths; + ~~~~~~~~~~~~~~ +!!! error TS2322: Type 'typeof import("/stub")' is not assignable to type 'number'. + const emptyAttributesValue: number = emptyAttributes; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'typeof import("/stub")' is not assignable to type 'number'. + const fromRootDirsValue: number = fromRootDirs; + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'typeof import("/generated/component.svelte")' is not assignable to type 'number'. + + import text from "x.svelte" with { type: "text" }; + const textValue: string = text; + + import missing from "./missing.svelte"; + const missingValue: number = missing; + +==== /stub.d.ts (0 errors) ==== + export {}; + +==== /generated/component.svelte.d.ts (0 errors) ==== + export {}; + +==== /src/script.ts (0 errors) ==== + export {}; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScript.errors.txt b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScript.errors.txt new file mode 100644 index 0000000000000..4720210c86ed8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScript.errors.txt @@ -0,0 +1,59 @@ +/src/main.ts(1,23): error TS2306: File '/stub.d.ts' is not a module. +/src/main.ts(2,29): error TS2306: File '/stub.d.ts' is not a module. +/src/main.ts(3,26): error TS2306: File '/generated/component.svelte.d.ts' is not a module. +/src/main.ts(4,26): error TS2306: File '/src/script.ts' is not a module. + + +==== /tsconfig.json (0 errors) ==== + { + "compilerOptions": { + "paths": { "x.svelte": ["./stub.d.ts"] }, + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] + } + +==== /ambient.d.ts (0 errors) ==== + declare module "*.svelte" { + const value: number; + export default value; + } + declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; + } + declare module "*.js" { + const value: number; + export default value; + } + +==== /src/main.ts (4 errors) ==== + import fromPaths from "x.svelte"; + ~~~~~~~~~~ +!!! error TS2306: File '/stub.d.ts' is not a module. + import emptyAttributes from "x.svelte" with {}; + ~~~~~~~~~~ +!!! error TS2306: File '/stub.d.ts' is not a module. + import fromRootDirs from "./component.svelte"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2306: File '/generated/component.svelte.d.ts' is not a module. + import fromRelative from "./script.js"; + ~~~~~~~~~~~~~ +!!! error TS2306: File '/src/script.ts' is not a module. + const fromPathsValue: string = fromPaths; + + import text from "x.svelte" with { type: "text" }; + const textValue: string = text; + + import missing from "./missing.svelte"; + const missingValue: number = missing; + +==== /stub.d.ts (0 errors) ==== + // A resolved script must not fall back to a pattern ambient module without attributes. + +==== /generated/component.svelte.d.ts (0 errors) ==== + // A script resolved through rootDirs. + +==== /src/script.ts (0 errors) ==== + // A script resolved through a relative import. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScriptWithoutPaths.errors.txt b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScriptWithoutPaths.errors.txt new file mode 100644 index 0000000000000..8e40142d4812e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/patternAmbientModuleResolvedScriptWithoutPaths.errors.txt @@ -0,0 +1,55 @@ +/src/main.ts(3,26): error TS2306: File '/generated/component.svelte.d.ts' is not a module. +/src/main.ts(4,26): error TS2306: File '/src/script.ts' is not a module. +/src/main.ts(5,7): error TS2322: Type 'number' is not assignable to type 'string'. + + +==== /tsconfig.json (0 errors) ==== + { + "compilerOptions": { + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] + } + +==== /ambient.d.ts (0 errors) ==== + declare module "*.svelte" { + const value: number; + export default value; + } + declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; + } + declare module "*.js" { + const value: number; + export default value; + } + +==== /src/main.ts (3 errors) ==== + import fromPaths from "x.svelte"; + import emptyAttributes from "x.svelte" with {}; + import fromRootDirs from "./component.svelte"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2306: File '/generated/component.svelte.d.ts' is not a module. + import fromRelative from "./script.js"; + ~~~~~~~~~~~~~ +!!! error TS2306: File '/src/script.ts' is not a module. + const fromPathsValue: string = fromPaths; + ~~~~~~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + import text from "x.svelte" with { type: "text" }; + const textValue: string = text; + + import missing from "./missing.svelte"; + const missingValue: number = missing; + +==== /stub.d.ts (0 errors) ==== + // This file is not resolved without a paths mapping. + +==== /generated/component.svelte.d.ts (0 errors) ==== + // A script resolved through rootDirs. + +==== /src/script.ts (0 errors) ==== + // A script resolved through a relative import. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.symbols b/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.symbols new file mode 100644 index 0000000000000..464c8f4346584 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.symbols @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/reverseMappedTypePrivateMembers.ts] //// + +=== reverseMappedTypePrivateMembers.ts === +// https://github.com/microsoft/TypeScript/issues/64593 + +class Entity { +>Entity : Symbol(Entity, Decl(reverseMappedTypePrivateMembers.ts, 0, 0)) + + private secret = 1; +>secret : Symbol(Entity.secret, Decl(reverseMappedTypePrivateMembers.ts, 2, 14)) + + protected hidden = 2; +>hidden : Symbol(Entity.hidden, Decl(reverseMappedTypePrivateMembers.ts, 3, 23)) + + markChanged(field: string): void {} +>markChanged : Symbol(Entity.markChanged, Decl(reverseMappedTypePrivateMembers.ts, 4, 25)) +>field : Symbol(field, Decl(reverseMappedTypePrivateMembers.ts, 5, 16)) +} +class Account extends Entity { +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) +>Entity : Symbol(Entity, Decl(reverseMappedTypePrivateMembers.ts, 0, 0)) + + emailAddress = ''; +>emailAddress : Symbol(Account.emailAddress, Decl(reverseMappedTypePrivateMembers.ts, 7, 30)) +} + +declare function getAccount(): Readonly; +>getAccount : Symbol(getAccount, Decl(reverseMappedTypePrivateMembers.ts, 9, 1)) +>T : Symbol(T, Decl(reverseMappedTypePrivateMembers.ts, 11, 28)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) +>T : Symbol(T, Decl(reverseMappedTypePrivateMembers.ts, 11, 28)) + +declare function getAccountPlain(): Readonly; +>getAccountPlain : Symbol(getAccountPlain, Decl(reverseMappedTypePrivateMembers.ts, 11, 80)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) + +export const plain = getAccountPlain() as Account; +>plain : Symbol(plain, Decl(reverseMappedTypePrivateMembers.ts, 14, 12)) +>getAccountPlain : Symbol(getAccountPlain, Decl(reverseMappedTypePrivateMembers.ts, 11, 80)) +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) + +export const explicit = getAccount() as Account; +>explicit : Symbol(explicit, Decl(reverseMappedTypePrivateMembers.ts, 15, 12)) +>getAccount : Symbol(getAccount, Decl(reverseMappedTypePrivateMembers.ts, 9, 1)) +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) + +export const inferred = getAccount() as Account; +>inferred : Symbol(inferred, Decl(reverseMappedTypePrivateMembers.ts, 16, 12)) +>getAccount : Symbol(getAccount, Decl(reverseMappedTypePrivateMembers.ts, 9, 1)) +>Account : Symbol(Account, Decl(reverseMappedTypePrivateMembers.ts, 6, 1)) + diff --git a/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.types b/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.types new file mode 100644 index 0000000000000..1807c6670e622 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/reverseMappedTypePrivateMembers.types @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/reverseMappedTypePrivateMembers.ts] //// + +=== reverseMappedTypePrivateMembers.ts === +// https://github.com/microsoft/TypeScript/issues/64593 + +class Entity { +>Entity : Entity + + private secret = 1; +>secret : number +>1 : 1 + + protected hidden = 2; +>hidden : number +>2 : 2 + + markChanged(field: string): void {} +>markChanged : (field: string) => void +>field : string +} +class Account extends Entity { +>Account : Account +>Entity : Entity + + emailAddress = ''; +>emailAddress : string +>'' : "" +} + +declare function getAccount(): Readonly; +>getAccount : () => Readonly + +declare function getAccountPlain(): Readonly; +>getAccountPlain : () => Readonly + +export const plain = getAccountPlain() as Account; +>plain : Account +>getAccountPlain() as Account : Account +>getAccountPlain() : Readonly +>getAccountPlain : () => Readonly + +export const explicit = getAccount() as Account; +>explicit : Account +>getAccount() as Account : Account +>getAccount() : Readonly +>getAccount : () => Readonly + +export const inferred = getAccount() as Account; +>inferred : Account +>getAccount() as Account : Account +>getAccount() : Readonly void; emailAddress: string; }> +>getAccount : () => Readonly + diff --git a/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.js b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.js new file mode 100644 index 0000000000000..4ccad04abab4a --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.js @@ -0,0 +1,87 @@ +//// [tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts] //// + +//// [importAliasMergedNamespace.ts] +namespace N { + export namespace M { + export class C {} + } + + import Local = M.C; + new Local(); +} + +namespace N { + import A = M.C; + new A(); + + import B = M; + new B.C(); + + export import D = M.C; + new D(); + + export import E = M; + new E.C(); + + import Explicit = N.M.C; + new Explicit(); +} + +namespace Outer { + export namespace Inner { + export namespace M { + export class C {} + } + } +} + +namespace Outer.Inner { + import A = M.C; + new A(); +} + + +//// [importAliasMergedNamespace.js] +"use strict"; +var N; +(function (N) { + let M; + (function (M) { + class C { + } + M.C = C; + })(M = N.M || (N.M = {})); + var Local = M.C; + new Local(); +})(N || (N = {})); +(function (N) { + var A = N.M.C; + new A(); + var B = N.M; + new B.C(); + N.D = N.M.C; + new N.D(); + N.E = N.M; + new N.E.C(); + var Explicit = N.M.C; + new Explicit(); +})(N || (N = {})); +var Outer; +(function (Outer) { + let Inner; + (function (Inner) { + let M; + (function (M) { + class C { + } + M.C = C; + })(M = Inner.M || (Inner.M = {})); + })(Inner = Outer.Inner || (Outer.Inner = {})); +})(Outer || (Outer = {})); +(function (Outer) { + var Inner; + (function (Inner) { + var A = Inner.M.C; + new A(); + })(Inner = Outer.Inner || (Outer.Inner = {})); +})(Outer || (Outer = {})); diff --git a/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.symbols b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.symbols new file mode 100644 index 0000000000000..131c4b9bec98d --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.symbols @@ -0,0 +1,97 @@ +//// [tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts] //// + +=== importAliasMergedNamespace.ts === +namespace N { +>N : Symbol(N, Decl(importAliasMergedNamespace.ts, 0, 0), Decl(importAliasMergedNamespace.ts, 7, 1)) + + export namespace M { +>M : Symbol(M, Decl(importAliasMergedNamespace.ts, 0, 13)) + + export class C {} +>C : Symbol(C, Decl(importAliasMergedNamespace.ts, 1, 24)) + } + + import Local = M.C; +>Local : Symbol(Local, Decl(importAliasMergedNamespace.ts, 3, 5)) +>M : Symbol(M, Decl(importAliasMergedNamespace.ts, 0, 13)) +>C : Symbol(Local, Decl(importAliasMergedNamespace.ts, 1, 24)) + + new Local(); +>Local : Symbol(Local, Decl(importAliasMergedNamespace.ts, 3, 5)) +} + +namespace N { +>N : Symbol(N, Decl(importAliasMergedNamespace.ts, 0, 0), Decl(importAliasMergedNamespace.ts, 7, 1)) + + import A = M.C; +>A : Symbol(A, Decl(importAliasMergedNamespace.ts, 9, 13)) +>M : Symbol(B, Decl(importAliasMergedNamespace.ts, 0, 13)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) + + new A(); +>A : Symbol(A, Decl(importAliasMergedNamespace.ts, 9, 13)) + + import B = M; +>B : Symbol(B, Decl(importAliasMergedNamespace.ts, 11, 12)) +>M : Symbol(B, Decl(importAliasMergedNamespace.ts, 0, 13)) + + new B.C(); +>B.C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) +>B : Symbol(B, Decl(importAliasMergedNamespace.ts, 11, 12)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) + + export import D = M.C; +>D : Symbol(D, Decl(importAliasMergedNamespace.ts, 14, 14)) +>M : Symbol(B, Decl(importAliasMergedNamespace.ts, 0, 13)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) + + new D(); +>D : Symbol(D, Decl(importAliasMergedNamespace.ts, 14, 14)) + + export import E = M; +>E : Symbol(E, Decl(importAliasMergedNamespace.ts, 17, 12)) +>M : Symbol(B, Decl(importAliasMergedNamespace.ts, 0, 13)) + + new E.C(); +>E.C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) +>E : Symbol(E, Decl(importAliasMergedNamespace.ts, 17, 12)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) + + import Explicit = N.M.C; +>Explicit : Symbol(Explicit, Decl(importAliasMergedNamespace.ts, 20, 14)) +>N : Symbol(N, Decl(importAliasMergedNamespace.ts, 0, 0), Decl(importAliasMergedNamespace.ts, 7, 1)) +>M : Symbol(B, Decl(importAliasMergedNamespace.ts, 0, 13)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 1, 24)) + + new Explicit(); +>Explicit : Symbol(Explicit, Decl(importAliasMergedNamespace.ts, 20, 14)) +} + +namespace Outer { +>Outer : Symbol(Outer, Decl(importAliasMergedNamespace.ts, 24, 1), Decl(importAliasMergedNamespace.ts, 32, 1)) + + export namespace Inner { +>Inner : Symbol(Inner, Decl(importAliasMergedNamespace.ts, 26, 17), Decl(importAliasMergedNamespace.ts, 34, 16)) + + export namespace M { +>M : Symbol(M, Decl(importAliasMergedNamespace.ts, 27, 28)) + + export class C {} +>C : Symbol(C, Decl(importAliasMergedNamespace.ts, 28, 28)) + } + } +} + +namespace Outer.Inner { +>Outer : Symbol(Outer, Decl(importAliasMergedNamespace.ts, 24, 1), Decl(importAliasMergedNamespace.ts, 32, 1)) +>Inner : Symbol(Inner, Decl(importAliasMergedNamespace.ts, 26, 17), Decl(importAliasMergedNamespace.ts, 34, 16)) + + import A = M.C; +>A : Symbol(A, Decl(importAliasMergedNamespace.ts, 34, 23)) +>M : Symbol(M, Decl(importAliasMergedNamespace.ts, 27, 28)) +>C : Symbol(A, Decl(importAliasMergedNamespace.ts, 28, 28)) + + new A(); +>A : Symbol(A, Decl(importAliasMergedNamespace.ts, 34, 23)) +} + diff --git a/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.types b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.types new file mode 100644 index 0000000000000..7dea5f21bf109 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/importAliasMergedNamespace.types @@ -0,0 +1,104 @@ +//// [tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts] //// + +=== importAliasMergedNamespace.ts === +namespace N { +>N : typeof N + + export namespace M { +>M : typeof M + + export class C {} +>C : C + } + + import Local = M.C; +>Local : typeof Local +>M : typeof M +>C : Local + + new Local(); +>new Local() : Local +>Local : typeof Local +} + +namespace N { +>N : typeof N + + import A = M.C; +>A : typeof A +>M : typeof B +>C : A + + new A(); +>new A() : A +>A : typeof A + + import B = M; +>B : typeof B +>M : typeof B + + new B.C(); +>new B.C() : A +>B.C : typeof A +>B : typeof B +>C : typeof A + + export import D = M.C; +>D : typeof A +>M : typeof B +>C : A + + new D(); +>new D() : A +>D : typeof A + + export import E = M; +>E : typeof B +>M : typeof B + + new E.C(); +>new E.C() : A +>E.C : typeof A +>E : typeof B +>C : typeof A + + import Explicit = N.M.C; +>Explicit : typeof A +>N : typeof N +>M : typeof B +>C : A + + new Explicit(); +>new Explicit() : A +>Explicit : typeof A +} + +namespace Outer { +>Outer : typeof Outer + + export namespace Inner { +>Inner : typeof Inner + + export namespace M { +>M : typeof M + + export class C {} +>C : C + } + } +} + +namespace Outer.Inner { +>Outer : typeof Outer +>Inner : typeof Inner + + import A = M.C; +>A : typeof A +>M : typeof M +>C : A + + new A(); +>new A() : A +>A : typeof A +} + diff --git a/tsc/testdata/baselines/reference/tsbuild/commandLine/help.js b/tsc/testdata/baselines/reference/tsbuild/commandLine/help.js index 378781eb3dd57..216d8b921ff3f 100644 --- a/tsc/testdata/baselines/reference/tsbuild/commandLine/help.js +++ b/tsc/testdata/baselines/reference/tsbuild/commandLine/help.js @@ -134,6 +134,9 @@ Generate pprof CPU/memory profiles to the given directory. --checkers Set the number of checkers per project. +--maxMemory +Set the memory limit in MiB for recycling checkers during no-emit type checking. + --runExternalCode Allow loading external content mapper plugins that execute code during compilation. diff --git a/tsc/testdata/baselines/reference/tsbuild/commandLine/locale.js b/tsc/testdata/baselines/reference/tsbuild/commandLine/locale.js index c797a521ce26f..d295c23aa9d6e 100644 --- a/tsc/testdata/baselines/reference/tsbuild/commandLine/locale.js +++ b/tsc/testdata/baselines/reference/tsbuild/commandLine/locale.js @@ -134,6 +134,9 @@ Generate pprof CPU/memory profiles to the given directory. --checkers Set the number of checkers per project. +--maxMemory +Set the memory limit in MiB for recycling checkers during no-emit type checking. + --runExternalCode Allow loading external content mapper plugins that execute code during compilation. diff --git a/tsc/testdata/baselines/reference/tsc/commandLine/help-all.js b/tsc/testdata/baselines/reference/tsc/commandLine/help-all.js index e63595b43e09c..a1556bb789ce3 100644 --- a/tsc/testdata/baselines/reference/tsc/commandLine/help-all.js +++ b/tsc/testdata/baselines/reference/tsc/commandLine/help-all.js @@ -38,6 +38,9 @@ Print names of files that are part of the compilation and then stop processing. --locale Set the language of the messaging from TypeScript. This does not affect emit. +--maxMemory +Set the memory limit in MiB for recycling checkers during no-emit type checking. + --pprofDir Generate pprof CPU/memory profiles to the given directory. diff --git a/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-index-signature-sibling-types-in-a-stable-order.js b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-index-signature-sibling-types-in-a-stable-order.js new file mode 100644 index 0000000000000..8a7f9ce43ae68 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-index-signature-sibling-types-in-a-stable-order.js @@ -0,0 +1,303 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/ambient.d.ts] *new* +declare const k1: string; +declare class C { + [k1]: { a: 1 }; + m1: { p1: 1 }; + m2: { p2: 2 }; + m3: { p3: 3 }; + m4: { p4: 4 }; + m5: { p5: 5 }; + m6: { p6: 6 }; + m7: { p7: 7 }; + m8: { p8: 8 }; + m9: { p9: 9 }; + m10: { p10: 10 }; +} +//// [/home/src/workspaces/project/main.ts] *new* +declare function expand(): { [K in keyof X]: X[K] }; +export const w = expand(); +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "ambient.d.ts"] +} + +tsgo --generateTrace /home/src/workspaces/project/trace --singleThreaded +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/out/main.d.ts] *new* +export declare const w: { + [x: string]: { + a: 1; + } | { + p1: 1; + } | { + p2: 2; + } | { + p3: 3; + } | { + p4: 4; + } | { + p5: 5; + } | { + p6: 6; + } | { + p7: 7; + } | { + p8: 8; + } | { + p9: 9; + } | { + p10: 10; + }; + m1: { + p1: 1; + }; + m2: { + p2: 2; + }; + m3: { + p3: 3; + }; + m4: { + p4: 4; + }; + m5: { + p5: 5; + }; + m6: { + p6: 6; + }; + m7: { + p7: 7; + }; + m8: { + p8: 8; + }; + m9: { + p9: 9; + }; + m10: { + p10: 10; + }; +}; + +//// [/home/src/workspaces/project/trace/legend.json] *new* +[ + { + "configFilePath": "/home/src/workspaces/project/tsconfig.json", + "tracePath": "/home/src/workspaces/project/trace/trace.json", + "typesPath": "/home/src/workspaces/project/trace/types_0.json", + "checkerId": 0 + } +] +//// [/home/src/workspaces/project/trace/trace.json] *new* +[ +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"process_name","args":{"name":"tsgo"}}, +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"Main"}}, +{"pid":1,"tid":1,"ph":"M","cat":"disabled-by-default-devtools.timeline","ts":1,"name":"TracingStartedInBrowser"}, +{"pid":1,"tid":1,"ph":"B","cat":"program","ts":2,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":181085097,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"parse","ts":3,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"parse","ts":4,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"B","cat":"parse","ts":5,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"E","cat":"parse","ts":6,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"parse","ts":7,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"parse","ts":8,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"program","ts":9,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":1,"ph":"B","cat":"bind","ts":10,"name":"bindSourceFiles"}, +{"pid":1,"tid":483181977,"ph":"B","cat":"bind","ts":11,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"E","cat":"bind","ts":12,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"bind","ts":13,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"bind","ts":14,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"bind","ts":15,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"bind","ts":16,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"bind","ts":17,"name":"bindSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"check","ts":18,"name":"checkSourceFiles"}, +{"pid":1,"tid":2,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"checker:0"}}, +{"pid":1,"tid":2,"ph":"B","cat":"check","ts":19,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":2,"ph":"E","cat":"check","ts":20,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"check","ts":21,"name":"checkSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":22,"name":"emit"}, +{"pid":1,"tid":100209852,"ph":"B","cat":"emit","ts":23,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":24,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":25,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"emit","ts":26,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":27,"name":"emit"} +] + +//// [/home/src/workspaces/project/trace/types_0.json] *new* +[{"id":1,"intrinsicName":"any","recursionId":0,"flags":["Any"]}, +{"id":2,"intrinsicName":"any","recursionId":1,"flags":["Any"]}, +{"id":3,"intrinsicName":"any","recursionId":2,"flags":["Any"]}, +{"id":4,"intrinsicName":"any","recursionId":3,"flags":["Any"]}, +{"id":5,"intrinsicName":"error","recursionId":4,"flags":["Any"]}, +{"id":6,"intrinsicName":"unresolved","recursionId":5,"flags":["Any"]}, +{"id":7,"intrinsicName":"any","recursionId":6,"flags":["Any"]}, +{"id":8,"intrinsicName":"intrinsic","recursionId":7,"flags":["Any"]}, +{"id":9,"intrinsicName":"unknown","recursionId":8,"flags":["Unknown"]}, +{"id":10,"intrinsicName":"undefined","recursionId":9,"flags":["Undefined"]}, +{"id":11,"intrinsicName":"undefined","recursionId":10,"flags":["Undefined"]}, +{"id":12,"intrinsicName":"undefined","recursionId":11,"flags":["Undefined"]}, +{"id":13,"intrinsicName":"null","recursionId":12,"flags":["Null"]}, +{"id":14,"intrinsicName":"string","recursionId":13,"flags":["String"]}, +{"id":15,"intrinsicName":"number","recursionId":14,"flags":["Number"]}, +{"id":16,"intrinsicName":"bigint","recursionId":15,"flags":["BigInt"]}, +{"id":17,"recursionId":16,"flags":["BooleanLiteral"],"display":"false"}, +{"id":18,"recursionId":17,"flags":["BooleanLiteral"],"display":"false"}, +{"id":19,"recursionId":18,"flags":["BooleanLiteral"],"display":"true"}, +{"id":20,"recursionId":19,"flags":["BooleanLiteral"],"display":"true"}, +{"id":21,"recursionId":20,"unionTypes":[17,19],"flags":["Boolean","Union"],"display":"boolean"}, +{"id":22,"intrinsicName":"symbol","recursionId":21,"flags":["ESSymbol"]}, +{"id":23,"intrinsicName":"void","recursionId":22,"flags":["Void"]}, +{"id":24,"intrinsicName":"never","recursionId":23,"flags":["Never"]}, +{"id":25,"intrinsicName":"never","recursionId":24,"flags":["Never"]}, +{"id":26,"intrinsicName":"never","recursionId":25,"flags":["Never"]}, +{"id":27,"intrinsicName":"never","recursionId":26,"flags":["Never"]}, +{"id":28,"intrinsicName":"object","recursionId":27,"flags":["NonPrimitive"]}, +{"id":29,"recursionId":28,"unionTypes":[14,15],"flags":["Union"],"display":"string | number"}, +{"id":30,"recursionId":29,"unionTypes":[14,15,22],"flags":["Union"],"display":"string | number | symbol"}, +{"id":31,"recursionId":30,"unionTypes":[15,16],"flags":["Union"],"display":"number | bigint"}, +{"id":32,"recursionId":31,"flags":["TemplateLiteral"],"display":"`${number}`"}, +{"id":33,"recursionId":32,"unionTypes":[10,13,14,15,16,17,19],"flags":["Union"],"display":"string | number | bigint | boolean | null | undefined"}, +{"id":34,"intrinsicName":"never","recursionId":33,"flags":["Never"]}, +{"id":35,"recursionId":34,"flags":["Object"],"display":"{}"}, +{"id":36,"recursionId":35,"flags":["Object"],"display":"{}"}, +{"id":37,"recursionId":36,"flags":["Object"],"display":"{}"}, +{"id":38,"symbolName":"__type","recursionId":37,"flags":["Object"],"display":"{}"}, +{"id":39,"recursionId":38,"flags":["Object"],"display":"{}"}, +{"id":40,"recursionId":39,"unionTypes":[10,13,39],"flags":["Union"],"display":"{} | null | undefined"}, +{"id":41,"recursionId":40,"flags":["Object"],"display":"{}"}, +{"id":42,"recursionId":41,"flags":["Object"],"display":"{}"}, +{"id":43,"recursionId":42,"flags":["Object"],"display":"{}"}, +{"id":44,"recursionId":43,"flags":["Object"],"display":"{}"}, +{"id":45,"recursionId":44,"flags":["Object"],"display":"{}"}, +{"id":46,"recursionId":45,"flags":["TypeParameter"]}, +{"id":47,"recursionId":46,"flags":["TypeParameter"]}, +{"id":48,"recursionId":47,"flags":["TypeParameter"]}, +{"id":49,"recursionId":48,"flags":["TypeParameter"]}, +{"id":50,"recursionId":49,"flags":["TypeParameter"]}, +{"id":51,"recursionId":50,"flags":["StringLiteral"],"display":"\"\""}, +{"id":52,"recursionId":51,"flags":["NumberLiteral"],"display":"0"}, +{"id":53,"recursionId":52,"flags":["BigIntLiteral"],"display":"0n"}, +{"id":54,"recursionId":53,"flags":["StringLiteral"],"display":"\"bigint\""}, +{"id":55,"recursionId":54,"flags":["StringLiteral"],"display":"\"boolean\""}, +{"id":56,"recursionId":55,"flags":["StringLiteral"],"display":"\"function\""}, +{"id":57,"recursionId":56,"flags":["StringLiteral"],"display":"\"number\""}, +{"id":58,"recursionId":57,"flags":["StringLiteral"],"display":"\"object\""}, +{"id":59,"recursionId":58,"flags":["StringLiteral"],"display":"\"string\""}, +{"id":60,"recursionId":59,"flags":["StringLiteral"],"display":"\"symbol\""}, +{"id":61,"recursionId":60,"flags":["StringLiteral"],"display":"\"undefined\""}, +{"id":62,"recursionId":61,"unionTypes":[54,55,56,57,58,59,60,61],"flags":["Union"],"display":"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" | \"string\" | \"symbol\" | \"undefined\""}, +{"id":63,"symbolName":"IArguments","recursionId":62,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":6,"character":1},"end":{"line":6,"character":24}},"flags":["Object"]}, +{"id":64,"symbolName":"globalThis","recursionId":63,"flags":["Object"],"display":"typeof globalThis"}, +{"id":65,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[66],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":66,"symbolName":"T","recursionId":65,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":17},"end":{"line":11,"character":18}},"flags":["TypeParameter"]}, +{"id":67,"symbolName":"Array","recursionId":64,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["TypeParameter"]}, +{"id":68,"symbolName":"Object","recursionId":66,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":8,"character":1},"end":{"line":8,"character":20}},"flags":["Object"]}, +{"id":69,"symbolName":"Function","recursionId":67,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":3,"character":1},"end":{"line":3,"character":22}},"flags":["Object"]}, +{"id":70,"symbolName":"CallableFunction","recursionId":68,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":4,"character":1},"end":{"line":4,"character":30}},"flags":["Object"]}, +{"id":71,"symbolName":"NewableFunction","recursionId":69,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":5,"character":1},"end":{"line":5,"character":29}},"flags":["Object"]}, +{"id":72,"symbolName":"String","recursionId":70,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":10,"character":1},"end":{"line":10,"character":34}},"flags":["Object"]}, +{"id":73,"symbolName":"Number","recursionId":71,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":7,"character":1},"end":{"line":7,"character":41}},"flags":["Object"]}, +{"id":74,"symbolName":"Boolean","recursionId":72,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":2,"character":1},"end":{"line":2,"character":21}},"flags":["Object"]}, +{"id":75,"symbolName":"RegExp","recursionId":73,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":9,"character":1},"end":{"line":9,"character":20}},"flags":["Object"]}, +{"id":76,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":77,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[2],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":78,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[79],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":79,"symbolName":"T","recursionId":75,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":25},"end":{"line":12,"character":26}},"flags":["TypeParameter"]}, +{"id":80,"symbolName":"ReadonlyArray","recursionId":74,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["TypeParameter"]}, +{"id":81,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":82,"symbolName":"X","recursionId":76,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":25},"end":{"line":1,"character":26}},"flags":["TypeParameter"]}, +{"id":83,"symbolName":"K","recursionId":77,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":34},"end":{"line":1,"character":46}},"flags":["TypeParameter"]}, +{"id":84,"recursionId":78,"keyofType":82,"flags":["Index"]}, +{"id":85,"symbolName":"__type","recursionId":79,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":31},"end":{"line":1,"character":55}},"flags":["Object"]}, +{"id":86,"recursionId":76,"indexedAccessObjectType":82,"indexedAccessIndexType":83,"flags":["IndexedAccess"]}, +{"id":87,"symbolName":"Symbol","recursionId":80,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":18,"character":13},"end":{"line":18,"character":38}},"flags":["Object"]}, +{"id":88,"symbolName":"SymbolConstructor","recursionId":81,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":13,"character":1},"end":{"line":17,"character":2}},"flags":["Object"]}, +{"id":89,"symbolName":"toStringTag","recursionId":82,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":16,"character":5},"end":{"line":16,"character":34}},"flags":["UniqueESSymbol"]}, +{"id":90,"symbolName":"expand","recursionId":83,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":1},"end":{"line":1,"character":56}},"flags":["Object"],"display":"() => { [K in keyof X]: X[K]; }"}, +{"id":91,"symbolName":"C","recursionId":84,"instantiatedType":91,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":2,"character":1},"end":{"line":14,"character":2}},"flags":["Object"]}, +{"id":92,"symbolName":"C","recursionId":84,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":2,"character":1},"end":{"line":14,"character":2}},"flags":["TypeParameter"]}, +{"id":93,"symbolName":"__type","recursionId":84,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":31},"end":{"line":1,"character":55}},"flags":["Object"]}, +{"id":94,"symbolName":"K","recursionId":77,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":34},"end":{"line":1,"character":46}},"flags":["TypeParameter"]}, +{"id":95,"recursionId":85,"keyofType":91,"flags":["Index"]}, +{"id":96,"symbolName":"__type","recursionId":86,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":3,"character":11},"end":{"line":3,"character":19}},"flags":["Object"],"display":"{ a: 1; }"}, +{"id":97,"symbolName":"__type","recursionId":87,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":4,"character":9},"end":{"line":4,"character":18}},"flags":["Object"],"display":"{ p1: 1; }"}, +{"id":98,"symbolName":"__type","recursionId":88,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":5,"character":9},"end":{"line":5,"character":18}},"flags":["Object"],"display":"{ p2: 2; }"}, +{"id":99,"symbolName":"__type","recursionId":89,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":6,"character":9},"end":{"line":6,"character":18}},"flags":["Object"],"display":"{ p3: 3; }"}, +{"id":100,"symbolName":"__type","recursionId":90,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":7,"character":9},"end":{"line":7,"character":18}},"flags":["Object"],"display":"{ p4: 4; }"}, +{"id":101,"symbolName":"__type","recursionId":91,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":8,"character":9},"end":{"line":8,"character":18}},"flags":["Object"],"display":"{ p5: 5; }"}, +{"id":102,"symbolName":"__type","recursionId":92,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":9,"character":9},"end":{"line":9,"character":18}},"flags":["Object"],"display":"{ p6: 6; }"}, +{"id":103,"symbolName":"__type","recursionId":93,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":10,"character":9},"end":{"line":10,"character":18}},"flags":["Object"],"display":"{ p7: 7; }"}, +{"id":104,"symbolName":"__type","recursionId":94,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":11,"character":9},"end":{"line":11,"character":18}},"flags":["Object"],"display":"{ p8: 8; }"}, +{"id":105,"symbolName":"__type","recursionId":95,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":12,"character":9},"end":{"line":12,"character":18}},"flags":["Object"],"display":"{ p9: 9; }"}, +{"id":106,"symbolName":"__type","recursionId":96,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":13,"character":10},"end":{"line":13,"character":21}},"flags":["Object"],"display":"{ p10: 10; }"}, +{"id":107,"recursionId":97,"flags":["NumberLiteral"],"display":"10"}, +{"id":108,"recursionId":98,"flags":["NumberLiteral"],"display":"10"}, +{"id":109,"recursionId":99,"flags":["NumberLiteral"],"display":"9"}, +{"id":110,"recursionId":100,"flags":["NumberLiteral"],"display":"9"}, +{"id":111,"recursionId":101,"flags":["NumberLiteral"],"display":"8"}, +{"id":112,"recursionId":102,"flags":["NumberLiteral"],"display":"8"}, +{"id":113,"recursionId":103,"flags":["NumberLiteral"],"display":"7"}, +{"id":114,"recursionId":104,"flags":["NumberLiteral"],"display":"7"}, +{"id":115,"recursionId":105,"flags":["NumberLiteral"],"display":"6"}, +{"id":116,"recursionId":106,"flags":["NumberLiteral"],"display":"6"}, +{"id":117,"recursionId":107,"flags":["NumberLiteral"],"display":"5"}, +{"id":118,"recursionId":108,"flags":["NumberLiteral"],"display":"5"}, +{"id":119,"recursionId":109,"flags":["NumberLiteral"],"display":"4"}, +{"id":120,"recursionId":110,"flags":["NumberLiteral"],"display":"4"}, +{"id":121,"recursionId":111,"flags":["NumberLiteral"],"display":"3"}, +{"id":122,"recursionId":112,"flags":["NumberLiteral"],"display":"3"}, +{"id":123,"recursionId":113,"flags":["NumberLiteral"],"display":"2"}, +{"id":124,"recursionId":114,"flags":["NumberLiteral"],"display":"2"}, +{"id":125,"recursionId":115,"flags":["NumberLiteral"],"display":"1"}, +{"id":126,"recursionId":116,"flags":["NumberLiteral"],"display":"1"}, +{"id":127,"recursionId":117,"unionTypes":[96,97,98,99,100,101,102,103,104,105,106],"flags":["Union"],"display":"{ a: 1; } | { p1: 1; } | { p2: 2; } | { p3: 3; } | { p4: 4; } | { p5: 5; } | { p6: 6; } | { p7: 7; } | { p8: 8; } | { p9: 9; } | { p10: 10; }"}, +{"id":128,"recursionId":118,"flags":["StringLiteral"],"display":"\"m1\""}, +{"id":129,"recursionId":119,"flags":["StringLiteral"],"display":"\"m2\""}, +{"id":130,"recursionId":120,"flags":["StringLiteral"],"display":"\"m3\""}, +{"id":131,"recursionId":121,"flags":["StringLiteral"],"display":"\"m4\""}, +{"id":132,"recursionId":122,"flags":["StringLiteral"],"display":"\"m5\""}, +{"id":133,"recursionId":123,"flags":["StringLiteral"],"display":"\"m6\""}, +{"id":134,"recursionId":124,"flags":["StringLiteral"],"display":"\"m7\""}, +{"id":135,"recursionId":125,"flags":["StringLiteral"],"display":"\"m8\""}, +{"id":136,"recursionId":126,"flags":["StringLiteral"],"display":"\"m9\""}, +{"id":137,"recursionId":127,"flags":["StringLiteral"],"display":"\"m10\""}, +{"id":138,"recursionId":128,"unionTypes":[14,15],"flags":["Union"],"display":"keyof C"}] + + diff --git a/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-static-index-signature-sibling-types-in-a-stable-order.js b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-static-index-signature-sibling-types-in-a-stable-order.js new file mode 100644 index 0000000000000..c5bbab613c082 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-static-index-signature-sibling-types-in-a-stable-order.js @@ -0,0 +1,281 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/ambient.d.ts] *new* +declare const k1: string; +declare class C { + static [k1]: { a: 1 }; + static m1: { p1: 1 }; + static m2: { p2: 2 }; + static m3: { p3: 3 }; + static m4: { p4: 4 }; + static m5: { p5: 5 }; + static m6: { p6: 6 }; + static m7: { p7: 7 }; + static m8: { p8: 8 }; + static m9: { p9: 9 }; + static m10: { p10: 10 }; +} +//// [/home/src/workspaces/project/main.ts] *new* +declare function expand(): { [K in keyof X]: X[K] }; +export const w = expand(); +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "ambient.d.ts"] +} + +tsgo --generateTrace /home/src/workspaces/project/trace --singleThreaded +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/out/main.d.ts] *new* +export declare const w: { + [x: string]: C; + m1: { + p1: 1; + }; + m2: { + p2: 2; + }; + m3: { + p3: 3; + }; + m4: { + p4: 4; + }; + m5: { + p5: 5; + }; + m6: { + p6: 6; + }; + m7: { + p7: 7; + }; + m8: { + p8: 8; + }; + m9: { + p9: 9; + }; + m10: { + p10: 10; + }; + prototype: C; +}; + +//// [/home/src/workspaces/project/trace/legend.json] *new* +[ + { + "configFilePath": "/home/src/workspaces/project/tsconfig.json", + "tracePath": "/home/src/workspaces/project/trace/trace.json", + "typesPath": "/home/src/workspaces/project/trace/types_0.json", + "checkerId": 0 + } +] +//// [/home/src/workspaces/project/trace/trace.json] *new* +[ +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"process_name","args":{"name":"tsgo"}}, +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"Main"}}, +{"pid":1,"tid":1,"ph":"M","cat":"disabled-by-default-devtools.timeline","ts":1,"name":"TracingStartedInBrowser"}, +{"pid":1,"tid":1,"ph":"B","cat":"program","ts":2,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":181085097,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"parse","ts":3,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"parse","ts":4,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"B","cat":"parse","ts":5,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"E","cat":"parse","ts":6,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"parse","ts":7,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"parse","ts":8,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"program","ts":9,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":1,"ph":"B","cat":"bind","ts":10,"name":"bindSourceFiles"}, +{"pid":1,"tid":483181977,"ph":"B","cat":"bind","ts":11,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":483181977,"ph":"E","cat":"bind","ts":12,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/ambient.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"bind","ts":13,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"bind","ts":14,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"bind","ts":15,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"bind","ts":16,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"bind","ts":17,"name":"bindSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"check","ts":18,"name":"checkSourceFiles"}, +{"pid":1,"tid":2,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"checker:0"}}, +{"pid":1,"tid":2,"ph":"B","cat":"check","ts":19,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":2,"ph":"E","cat":"check","ts":20,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"check","ts":21,"name":"checkSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":22,"name":"emit"}, +{"pid":1,"tid":100209852,"ph":"B","cat":"emit","ts":23,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":24,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":25,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"emit","ts":26,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":27,"name":"emit"} +] + +//// [/home/src/workspaces/project/trace/types_0.json] *new* +[{"id":1,"intrinsicName":"any","recursionId":0,"flags":["Any"]}, +{"id":2,"intrinsicName":"any","recursionId":1,"flags":["Any"]}, +{"id":3,"intrinsicName":"any","recursionId":2,"flags":["Any"]}, +{"id":4,"intrinsicName":"any","recursionId":3,"flags":["Any"]}, +{"id":5,"intrinsicName":"error","recursionId":4,"flags":["Any"]}, +{"id":6,"intrinsicName":"unresolved","recursionId":5,"flags":["Any"]}, +{"id":7,"intrinsicName":"any","recursionId":6,"flags":["Any"]}, +{"id":8,"intrinsicName":"intrinsic","recursionId":7,"flags":["Any"]}, +{"id":9,"intrinsicName":"unknown","recursionId":8,"flags":["Unknown"]}, +{"id":10,"intrinsicName":"undefined","recursionId":9,"flags":["Undefined"]}, +{"id":11,"intrinsicName":"undefined","recursionId":10,"flags":["Undefined"]}, +{"id":12,"intrinsicName":"undefined","recursionId":11,"flags":["Undefined"]}, +{"id":13,"intrinsicName":"null","recursionId":12,"flags":["Null"]}, +{"id":14,"intrinsicName":"string","recursionId":13,"flags":["String"]}, +{"id":15,"intrinsicName":"number","recursionId":14,"flags":["Number"]}, +{"id":16,"intrinsicName":"bigint","recursionId":15,"flags":["BigInt"]}, +{"id":17,"recursionId":16,"flags":["BooleanLiteral"],"display":"false"}, +{"id":18,"recursionId":17,"flags":["BooleanLiteral"],"display":"false"}, +{"id":19,"recursionId":18,"flags":["BooleanLiteral"],"display":"true"}, +{"id":20,"recursionId":19,"flags":["BooleanLiteral"],"display":"true"}, +{"id":21,"recursionId":20,"unionTypes":[17,19],"flags":["Boolean","Union"],"display":"boolean"}, +{"id":22,"intrinsicName":"symbol","recursionId":21,"flags":["ESSymbol"]}, +{"id":23,"intrinsicName":"void","recursionId":22,"flags":["Void"]}, +{"id":24,"intrinsicName":"never","recursionId":23,"flags":["Never"]}, +{"id":25,"intrinsicName":"never","recursionId":24,"flags":["Never"]}, +{"id":26,"intrinsicName":"never","recursionId":25,"flags":["Never"]}, +{"id":27,"intrinsicName":"never","recursionId":26,"flags":["Never"]}, +{"id":28,"intrinsicName":"object","recursionId":27,"flags":["NonPrimitive"]}, +{"id":29,"recursionId":28,"unionTypes":[14,15],"flags":["Union"],"display":"string | number"}, +{"id":30,"recursionId":29,"unionTypes":[14,15,22],"flags":["Union"],"display":"string | number | symbol"}, +{"id":31,"recursionId":30,"unionTypes":[15,16],"flags":["Union"],"display":"number | bigint"}, +{"id":32,"recursionId":31,"flags":["TemplateLiteral"],"display":"`${number}`"}, +{"id":33,"recursionId":32,"unionTypes":[10,13,14,15,16,17,19],"flags":["Union"],"display":"string | number | bigint | boolean | null | undefined"}, +{"id":34,"intrinsicName":"never","recursionId":33,"flags":["Never"]}, +{"id":35,"recursionId":34,"flags":["Object"],"display":"{}"}, +{"id":36,"recursionId":35,"flags":["Object"],"display":"{}"}, +{"id":37,"recursionId":36,"flags":["Object"],"display":"{}"}, +{"id":38,"symbolName":"__type","recursionId":37,"flags":["Object"],"display":"{}"}, +{"id":39,"recursionId":38,"flags":["Object"],"display":"{}"}, +{"id":40,"recursionId":39,"unionTypes":[10,13,39],"flags":["Union"],"display":"{} | null | undefined"}, +{"id":41,"recursionId":40,"flags":["Object"],"display":"{}"}, +{"id":42,"recursionId":41,"flags":["Object"],"display":"{}"}, +{"id":43,"recursionId":42,"flags":["Object"],"display":"{}"}, +{"id":44,"recursionId":43,"flags":["Object"],"display":"{}"}, +{"id":45,"recursionId":44,"flags":["Object"],"display":"{}"}, +{"id":46,"recursionId":45,"flags":["TypeParameter"]}, +{"id":47,"recursionId":46,"flags":["TypeParameter"]}, +{"id":48,"recursionId":47,"flags":["TypeParameter"]}, +{"id":49,"recursionId":48,"flags":["TypeParameter"]}, +{"id":50,"recursionId":49,"flags":["TypeParameter"]}, +{"id":51,"recursionId":50,"flags":["StringLiteral"],"display":"\"\""}, +{"id":52,"recursionId":51,"flags":["NumberLiteral"],"display":"0"}, +{"id":53,"recursionId":52,"flags":["BigIntLiteral"],"display":"0n"}, +{"id":54,"recursionId":53,"flags":["StringLiteral"],"display":"\"bigint\""}, +{"id":55,"recursionId":54,"flags":["StringLiteral"],"display":"\"boolean\""}, +{"id":56,"recursionId":55,"flags":["StringLiteral"],"display":"\"function\""}, +{"id":57,"recursionId":56,"flags":["StringLiteral"],"display":"\"number\""}, +{"id":58,"recursionId":57,"flags":["StringLiteral"],"display":"\"object\""}, +{"id":59,"recursionId":58,"flags":["StringLiteral"],"display":"\"string\""}, +{"id":60,"recursionId":59,"flags":["StringLiteral"],"display":"\"symbol\""}, +{"id":61,"recursionId":60,"flags":["StringLiteral"],"display":"\"undefined\""}, +{"id":62,"recursionId":61,"unionTypes":[54,55,56,57,58,59,60,61],"flags":["Union"],"display":"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" | \"string\" | \"symbol\" | \"undefined\""}, +{"id":63,"symbolName":"IArguments","recursionId":62,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":6,"character":1},"end":{"line":6,"character":24}},"flags":["Object"]}, +{"id":64,"symbolName":"globalThis","recursionId":63,"flags":["Object"],"display":"typeof globalThis"}, +{"id":65,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[66],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":66,"symbolName":"T","recursionId":65,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":17},"end":{"line":11,"character":18}},"flags":["TypeParameter"]}, +{"id":67,"symbolName":"Array","recursionId":64,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["TypeParameter"]}, +{"id":68,"symbolName":"Object","recursionId":66,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":8,"character":1},"end":{"line":8,"character":20}},"flags":["Object"]}, +{"id":69,"symbolName":"Function","recursionId":67,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":3,"character":1},"end":{"line":3,"character":22}},"flags":["Object"]}, +{"id":70,"symbolName":"CallableFunction","recursionId":68,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":4,"character":1},"end":{"line":4,"character":30}},"flags":["Object"]}, +{"id":71,"symbolName":"NewableFunction","recursionId":69,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":5,"character":1},"end":{"line":5,"character":29}},"flags":["Object"]}, +{"id":72,"symbolName":"String","recursionId":70,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":10,"character":1},"end":{"line":10,"character":34}},"flags":["Object"]}, +{"id":73,"symbolName":"Number","recursionId":71,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":7,"character":1},"end":{"line":7,"character":41}},"flags":["Object"]}, +{"id":74,"symbolName":"Boolean","recursionId":72,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":2,"character":1},"end":{"line":2,"character":21}},"flags":["Object"]}, +{"id":75,"symbolName":"RegExp","recursionId":73,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":9,"character":1},"end":{"line":9,"character":20}},"flags":["Object"]}, +{"id":76,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":77,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[2],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":78,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[79],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":79,"symbolName":"T","recursionId":75,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":25},"end":{"line":12,"character":26}},"flags":["TypeParameter"]}, +{"id":80,"symbolName":"ReadonlyArray","recursionId":74,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["TypeParameter"]}, +{"id":81,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":82,"symbolName":"X","recursionId":76,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":25},"end":{"line":1,"character":26}},"flags":["TypeParameter"]}, +{"id":83,"symbolName":"K","recursionId":77,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":34},"end":{"line":1,"character":46}},"flags":["TypeParameter"]}, +{"id":84,"recursionId":78,"keyofType":82,"flags":["Index"]}, +{"id":85,"symbolName":"__type","recursionId":79,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":31},"end":{"line":1,"character":55}},"flags":["Object"]}, +{"id":86,"recursionId":76,"indexedAccessObjectType":82,"indexedAccessIndexType":83,"flags":["IndexedAccess"]}, +{"id":87,"symbolName":"Symbol","recursionId":80,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":18,"character":13},"end":{"line":18,"character":38}},"flags":["Object"]}, +{"id":88,"symbolName":"SymbolConstructor","recursionId":81,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":13,"character":1},"end":{"line":17,"character":2}},"flags":["Object"]}, +{"id":89,"symbolName":"toStringTag","recursionId":82,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":16,"character":5},"end":{"line":16,"character":34}},"flags":["UniqueESSymbol"]}, +{"id":90,"symbolName":"expand","recursionId":83,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":1},"end":{"line":1,"character":56}},"flags":["Object"],"display":"() => { [K in keyof X]: X[K]; }"}, +{"id":91,"symbolName":"C","recursionId":84,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":2,"character":1},"end":{"line":14,"character":2}},"flags":["Object"],"display":"typeof C"}, +{"id":92,"symbolName":"C","recursionId":85,"instantiatedType":92,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":2,"character":1},"end":{"line":14,"character":2}},"flags":["Object"]}, +{"id":93,"symbolName":"C","recursionId":85,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":2,"character":1},"end":{"line":14,"character":2}},"flags":["TypeParameter"]}, +{"id":94,"symbolName":"__type","recursionId":84,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":31},"end":{"line":1,"character":55}},"flags":["Object"]}, +{"id":95,"symbolName":"K","recursionId":77,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":1,"character":34},"end":{"line":1,"character":46}},"flags":["TypeParameter"]}, +{"id":96,"symbolName":"__type","recursionId":86,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":3,"character":18},"end":{"line":3,"character":26}},"flags":["Object"],"display":"{ a: 1; }"}, +{"id":97,"symbolName":"__type","recursionId":87,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":4,"character":16},"end":{"line":4,"character":25}},"flags":["Object"],"display":"{ p1: 1; }"}, +{"id":98,"symbolName":"__type","recursionId":88,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":5,"character":16},"end":{"line":5,"character":25}},"flags":["Object"],"display":"{ p2: 2; }"}, +{"id":99,"symbolName":"__type","recursionId":89,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":6,"character":16},"end":{"line":6,"character":25}},"flags":["Object"],"display":"{ p3: 3; }"}, +{"id":100,"symbolName":"__type","recursionId":90,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":7,"character":16},"end":{"line":7,"character":25}},"flags":["Object"],"display":"{ p4: 4; }"}, +{"id":101,"symbolName":"__type","recursionId":91,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":8,"character":16},"end":{"line":8,"character":25}},"flags":["Object"],"display":"{ p5: 5; }"}, +{"id":102,"symbolName":"__type","recursionId":92,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":9,"character":16},"end":{"line":9,"character":25}},"flags":["Object"],"display":"{ p6: 6; }"}, +{"id":103,"symbolName":"__type","recursionId":93,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":10,"character":16},"end":{"line":10,"character":25}},"flags":["Object"],"display":"{ p7: 7; }"}, +{"id":104,"symbolName":"__type","recursionId":94,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":11,"character":16},"end":{"line":11,"character":25}},"flags":["Object"],"display":"{ p8: 8; }"}, +{"id":105,"symbolName":"__type","recursionId":95,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":12,"character":16},"end":{"line":12,"character":25}},"flags":["Object"],"display":"{ p9: 9; }"}, +{"id":106,"symbolName":"__type","recursionId":96,"firstDeclaration":{"path":"/home/src/workspaces/project/ambient.d.ts","start":{"line":13,"character":17},"end":{"line":13,"character":28}},"flags":["Object"],"display":"{ p10: 10; }"}, +{"id":107,"recursionId":97,"flags":["NumberLiteral"],"display":"10"}, +{"id":108,"recursionId":98,"flags":["NumberLiteral"],"display":"10"}, +{"id":109,"recursionId":99,"flags":["NumberLiteral"],"display":"9"}, +{"id":110,"recursionId":100,"flags":["NumberLiteral"],"display":"9"}, +{"id":111,"recursionId":101,"flags":["NumberLiteral"],"display":"8"}, +{"id":112,"recursionId":102,"flags":["NumberLiteral"],"display":"8"}, +{"id":113,"recursionId":103,"flags":["NumberLiteral"],"display":"7"}, +{"id":114,"recursionId":104,"flags":["NumberLiteral"],"display":"7"}, +{"id":115,"recursionId":105,"flags":["NumberLiteral"],"display":"6"}, +{"id":116,"recursionId":106,"flags":["NumberLiteral"],"display":"6"}, +{"id":117,"recursionId":107,"flags":["NumberLiteral"],"display":"5"}, +{"id":118,"recursionId":108,"flags":["NumberLiteral"],"display":"5"}, +{"id":119,"recursionId":109,"flags":["NumberLiteral"],"display":"4"}, +{"id":120,"recursionId":110,"flags":["NumberLiteral"],"display":"4"}, +{"id":121,"recursionId":111,"flags":["NumberLiteral"],"display":"3"}, +{"id":122,"recursionId":112,"flags":["NumberLiteral"],"display":"3"}, +{"id":123,"recursionId":113,"flags":["NumberLiteral"],"display":"2"}, +{"id":124,"recursionId":114,"flags":["NumberLiteral"],"display":"2"}, +{"id":125,"recursionId":115,"flags":["NumberLiteral"],"display":"1"}, +{"id":126,"recursionId":116,"flags":["NumberLiteral"],"display":"1"}, +{"id":127,"recursionId":117,"flags":["StringLiteral"],"display":"\"m1\""}, +{"id":128,"recursionId":118,"flags":["StringLiteral"],"display":"\"m2\""}, +{"id":129,"recursionId":119,"flags":["StringLiteral"],"display":"\"m3\""}, +{"id":130,"recursionId":120,"flags":["StringLiteral"],"display":"\"m4\""}, +{"id":131,"recursionId":121,"flags":["StringLiteral"],"display":"\"m5\""}, +{"id":132,"recursionId":122,"flags":["StringLiteral"],"display":"\"m6\""}, +{"id":133,"recursionId":123,"flags":["StringLiteral"],"display":"\"m7\""}, +{"id":134,"recursionId":124,"flags":["StringLiteral"],"display":"\"m8\""}, +{"id":135,"recursionId":125,"flags":["StringLiteral"],"display":"\"m9\""}, +{"id":136,"recursionId":126,"flags":["StringLiteral"],"display":"\"m10\""}, +{"id":137,"recursionId":127,"flags":["StringLiteral"],"display":"\"prototype\""}] + + diff --git a/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-types-in-a-stable-order.js b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-types-in-a-stable-order.js new file mode 100644 index 0000000000000..0cbad41700637 --- /dev/null +++ b/tsc/testdata/baselines/reference/tsc/generateTrace/generateTrace-creates-types-in-a-stable-order.js @@ -0,0 +1,554 @@ +currentDirectory::/home/src/workspaces/project +useCaseSensitiveFileNames::true +Input:: +//// [/home/src/workspaces/project/fields.d.ts] *new* +export type Fields = T extends { a: infer A; b: infer B; c: infer C; d: infer D; e: infer E; f: infer F; g: infer G; h: infer H; i: infer I; j: infer J; k: infer K; l: infer L } ? [A, B, C, D, E, F, G, H, I, J, K, L] : never; +//// [/home/src/workspaces/project/gadget.d.ts] *new* +export interface Gadget { + id: number; +} +//// [/home/src/workspaces/project/locales/ar.d.ts] *new* +declare const _default: () => { locale: "ar" }; +export = _default; + +//// [/home/src/workspaces/project/locales/de.d.ts] *new* +declare const _default: () => { locale: "de" }; +export = _default; + +//// [/home/src/workspaces/project/locales/en.d.ts] *new* +declare const _default: () => { locale: "en" }; +export = _default; + +//// [/home/src/workspaces/project/locales/es.d.ts] *new* +declare const _default: () => { locale: "es" }; +export = _default; + +//// [/home/src/workspaces/project/locales/fr.d.ts] *new* +declare const _default: () => { locale: "fr" }; +export = _default; + +//// [/home/src/workspaces/project/locales/he.d.ts] *new* +declare const _default: () => { locale: "he" }; +export = _default; + +//// [/home/src/workspaces/project/locales/index.d.ts] *new* +export { default as ar } from "./ar"; +export { default as de } from "./de"; +export { default as en } from "./en"; +export { default as es } from "./es"; +export { default as fr } from "./fr"; +export { default as he } from "./he"; +export { default as it } from "./it"; +export { default as ja } from "./ja"; +export { default as ko } from "./ko"; +export { default as nl } from "./nl"; +export { default as pl } from "./pl"; +export { default as pt } from "./pt"; + +//// [/home/src/workspaces/project/locales/it.d.ts] *new* +declare const _default: () => { locale: "it" }; +export = _default; + +//// [/home/src/workspaces/project/locales/ja.d.ts] *new* +declare const _default: () => { locale: "ja" }; +export = _default; + +//// [/home/src/workspaces/project/locales/ko.d.ts] *new* +declare const _default: () => { locale: "ko" }; +export = _default; + +//// [/home/src/workspaces/project/locales/nl.d.ts] *new* +declare const _default: () => { locale: "nl" }; +export = _default; + +//// [/home/src/workspaces/project/locales/pl.d.ts] *new* +declare const _default: () => { locale: "pl" }; +export = _default; + +//// [/home/src/workspaces/project/locales/pt.d.ts] *new* +declare const _default: () => { locale: "pt" }; +export = _default; + +//// [/home/src/workspaces/project/main.ts] *new* +import * as parts from "./parts/index"; +import { Fields } from "./fields"; +import { makeWidget } from "./widget"; + +export const fields: Fields<{ a: 1; b: 2; c: 3; d: 4; e: 5; f: 6; g: 7; h: 8; i: 9; j: 10; k: 11; l: 12 }> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; +export const allParts = parts; +export const widget = makeWidget(); +//// [/home/src/workspaces/project/parts/alpha.d.ts] *new* +export declare const alpha: { name: "alpha" }; + +//// [/home/src/workspaces/project/parts/beta.d.ts] *new* +export declare const beta: { name: "beta" }; + +//// [/home/src/workspaces/project/parts/delta.d.ts] *new* +export declare const delta: { name: "delta" }; + +//// [/home/src/workspaces/project/parts/epsilon.d.ts] *new* +export declare const epsilon: { name: "epsilon" }; + +//// [/home/src/workspaces/project/parts/eta.d.ts] *new* +export declare const eta: { name: "eta" }; + +//// [/home/src/workspaces/project/parts/gamma.d.ts] *new* +export declare const gamma: { name: "gamma" }; + +//// [/home/src/workspaces/project/parts/index.d.ts] *new* +import * as alpha from "./alpha"; +import * as beta from "./beta"; +import * as gamma from "./gamma"; +import * as delta from "./delta"; +import * as epsilon from "./epsilon"; +import * as zeta from "./zeta"; +import * as eta from "./eta"; +import * as theta from "./theta"; +import * as iota from "./iota"; +import * as kappa from "./kappa"; +import * as lambda from "./lambda"; +import * as mu from "./mu"; +export { alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota, kappa, lambda, mu }; + +//// [/home/src/workspaces/project/parts/iota.d.ts] *new* +export declare const iota: { name: "iota" }; + +//// [/home/src/workspaces/project/parts/kappa.d.ts] *new* +export declare const kappa: { name: "kappa" }; + +//// [/home/src/workspaces/project/parts/lambda.d.ts] *new* +export declare const lambda: { name: "lambda" }; + +//// [/home/src/workspaces/project/parts/mu.d.ts] *new* +export declare const mu: { name: "mu" }; + +//// [/home/src/workspaces/project/parts/theta.d.ts] *new* +export declare const theta: { name: "theta" }; + +//// [/home/src/workspaces/project/parts/zeta.d.ts] *new* +export declare const zeta: { name: "zeta" }; + +//// [/home/src/workspaces/project/tsconfig.json] *new* +{ + "compilerOptions": { + "strict": true, + "skipLibCheck": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "out" + }, + "files": ["main.ts", "locales/index.d.ts"] +} +//// [/home/src/workspaces/project/widget.d.ts] *new* +import { Gadget } from "./gadget"; +export declare function makeWidget(): Gadget; + +tsgo --generateTrace /home/src/workspaces/project/trace --singleThreaded +ExitStatus:: Success +Output:: +//// [/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts] *Lib* +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +interface SymbolConstructor { + (desc?: string | number): symbol; + for(name: string): symbol; + readonly toStringTag: symbol; +} +declare var Symbol: SymbolConstructor; +interface Symbol { + readonly [Symbol.toStringTag]: string; +} +declare const console: { log(msg: any): void; }; +//// [/home/src/workspaces/project/out/main.d.ts] *new* +import * as parts from "./parts/index"; +import { Fields } from "./fields"; +export declare const fields: Fields<{ + a: 1; + b: 2; + c: 3; + d: 4; + e: 5; + f: 6; + g: 7; + h: 8; + i: 9; + j: 10; + k: 11; + l: 12; +}>; +export declare const allParts: typeof parts; +export declare const widget: import("./gadget").Gadget; + +//// [/home/src/workspaces/project/trace/legend.json] *new* +[ + { + "configFilePath": "/home/src/workspaces/project/tsconfig.json", + "tracePath": "/home/src/workspaces/project/trace/trace.json", + "typesPath": "/home/src/workspaces/project/trace/types_0.json", + "checkerId": 0 + } +] +//// [/home/src/workspaces/project/trace/trace.json] *new* +[ +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"process_name","args":{"name":"tsgo"}}, +{"pid":1,"tid":1,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"Main"}}, +{"pid":1,"tid":1,"ph":"M","cat":"disabled-by-default-devtools.timeline","ts":1,"name":"TracingStartedInBrowser"}, +{"pid":1,"tid":1,"ph":"B","cat":"program","ts":2,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":181085097,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"parse","ts":3,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"parse","ts":4,"name":"createSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":373807619,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/index.d.ts"}}, +{"pid":1,"tid":373807619,"ph":"B","cat":"parse","ts":5,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/index.d.ts"}}, +{"pid":1,"tid":373807619,"ph":"E","cat":"parse","ts":6,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/index.d.ts"}}, +{"pid":1,"tid":872471718,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/pt.d.ts"}}, +{"pid":1,"tid":872471718,"ph":"B","cat":"parse","ts":7,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/pt.d.ts"}}, +{"pid":1,"tid":872471718,"ph":"E","cat":"parse","ts":8,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/pt.d.ts"}}, +{"pid":1,"tid":944314902,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/pl.d.ts"}}, +{"pid":1,"tid":944314902,"ph":"B","cat":"parse","ts":9,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/pl.d.ts"}}, +{"pid":1,"tid":944314902,"ph":"E","cat":"parse","ts":10,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/pl.d.ts"}}, +{"pid":1,"tid":255493617,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/nl.d.ts"}}, +{"pid":1,"tid":255493617,"ph":"B","cat":"parse","ts":11,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/nl.d.ts"}}, +{"pid":1,"tid":255493617,"ph":"E","cat":"parse","ts":12,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/nl.d.ts"}}, +{"pid":1,"tid":376655012,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/ko.d.ts"}}, +{"pid":1,"tid":376655012,"ph":"B","cat":"parse","ts":13,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ko.d.ts"}}, +{"pid":1,"tid":376655012,"ph":"E","cat":"parse","ts":14,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ko.d.ts"}}, +{"pid":1,"tid":345504644,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/ja.d.ts"}}, +{"pid":1,"tid":345504644,"ph":"B","cat":"parse","ts":15,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ja.d.ts"}}, +{"pid":1,"tid":345504644,"ph":"E","cat":"parse","ts":16,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ja.d.ts"}}, +{"pid":1,"tid":450563598,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/it.d.ts"}}, +{"pid":1,"tid":450563598,"ph":"B","cat":"parse","ts":17,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/it.d.ts"}}, +{"pid":1,"tid":450563598,"ph":"E","cat":"parse","ts":18,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/it.d.ts"}}, +{"pid":1,"tid":141527137,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/he.d.ts"}}, +{"pid":1,"tid":141527137,"ph":"B","cat":"parse","ts":19,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/he.d.ts"}}, +{"pid":1,"tid":141527137,"ph":"E","cat":"parse","ts":20,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/he.d.ts"}}, +{"pid":1,"tid":883991556,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/fr.d.ts"}}, +{"pid":1,"tid":883991556,"ph":"B","cat":"parse","ts":21,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/fr.d.ts"}}, +{"pid":1,"tid":883991556,"ph":"E","cat":"parse","ts":22,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/fr.d.ts"}}, +{"pid":1,"tid":455027228,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/es.d.ts"}}, +{"pid":1,"tid":455027228,"ph":"B","cat":"parse","ts":23,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/es.d.ts"}}, +{"pid":1,"tid":455027228,"ph":"E","cat":"parse","ts":24,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/es.d.ts"}}, +{"pid":1,"tid":616565165,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/en.d.ts"}}, +{"pid":1,"tid":616565165,"ph":"B","cat":"parse","ts":25,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/en.d.ts"}}, +{"pid":1,"tid":616565165,"ph":"E","cat":"parse","ts":26,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/en.d.ts"}}, +{"pid":1,"tid":930019567,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/de.d.ts"}}, +{"pid":1,"tid":930019567,"ph":"B","cat":"parse","ts":27,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/de.d.ts"}}, +{"pid":1,"tid":930019567,"ph":"E","cat":"parse","ts":28,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/de.d.ts"}}, +{"pid":1,"tid":743285103,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/locales/ar.d.ts"}}, +{"pid":1,"tid":743285103,"ph":"B","cat":"parse","ts":29,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ar.d.ts"}}, +{"pid":1,"tid":743285103,"ph":"E","cat":"parse","ts":30,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/locales/ar.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"parse","ts":31,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"parse","ts":32,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":540121770,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/widget.d.ts"}}, +{"pid":1,"tid":540121770,"ph":"B","cat":"parse","ts":33,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/widget.d.ts"}}, +{"pid":1,"tid":540121770,"ph":"E","cat":"parse","ts":34,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/widget.d.ts"}}, +{"pid":1,"tid":411501950,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/gadget.d.ts"}}, +{"pid":1,"tid":411501950,"ph":"B","cat":"parse","ts":35,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/gadget.d.ts"}}, +{"pid":1,"tid":411501950,"ph":"E","cat":"parse","ts":36,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/gadget.d.ts"}}, +{"pid":1,"tid":454685436,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/fields.d.ts"}}, +{"pid":1,"tid":454685436,"ph":"B","cat":"parse","ts":37,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/fields.d.ts"}}, +{"pid":1,"tid":454685436,"ph":"E","cat":"parse","ts":38,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/fields.d.ts"}}, +{"pid":1,"tid":911467565,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/index.d.ts"}}, +{"pid":1,"tid":911467565,"ph":"B","cat":"parse","ts":39,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/index.d.ts"}}, +{"pid":1,"tid":911467565,"ph":"E","cat":"parse","ts":40,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/index.d.ts"}}, +{"pid":1,"tid":3296854,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/mu.d.ts"}}, +{"pid":1,"tid":3296854,"ph":"B","cat":"parse","ts":41,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/mu.d.ts"}}, +{"pid":1,"tid":3296854,"ph":"E","cat":"parse","ts":42,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/mu.d.ts"}}, +{"pid":1,"tid":172708103,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/lambda.d.ts"}}, +{"pid":1,"tid":172708103,"ph":"B","cat":"parse","ts":43,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/lambda.d.ts"}}, +{"pid":1,"tid":172708103,"ph":"E","cat":"parse","ts":44,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/lambda.d.ts"}}, +{"pid":1,"tid":887942961,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/kappa.d.ts"}}, +{"pid":1,"tid":887942961,"ph":"B","cat":"parse","ts":45,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/kappa.d.ts"}}, +{"pid":1,"tid":887942961,"ph":"E","cat":"parse","ts":46,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/kappa.d.ts"}}, +{"pid":1,"tid":757955809,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/iota.d.ts"}}, +{"pid":1,"tid":757955809,"ph":"B","cat":"parse","ts":47,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/iota.d.ts"}}, +{"pid":1,"tid":757955809,"ph":"E","cat":"parse","ts":48,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/iota.d.ts"}}, +{"pid":1,"tid":909662959,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/theta.d.ts"}}, +{"pid":1,"tid":909662959,"ph":"B","cat":"parse","ts":49,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/theta.d.ts"}}, +{"pid":1,"tid":909662959,"ph":"E","cat":"parse","ts":50,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/theta.d.ts"}}, +{"pid":1,"tid":86181510,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/eta.d.ts"}}, +{"pid":1,"tid":86181510,"ph":"B","cat":"parse","ts":51,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/eta.d.ts"}}, +{"pid":1,"tid":86181510,"ph":"E","cat":"parse","ts":52,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/eta.d.ts"}}, +{"pid":1,"tid":813403241,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/zeta.d.ts"}}, +{"pid":1,"tid":813403241,"ph":"B","cat":"parse","ts":53,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/zeta.d.ts"}}, +{"pid":1,"tid":813403241,"ph":"E","cat":"parse","ts":54,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/zeta.d.ts"}}, +{"pid":1,"tid":758077059,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/epsilon.d.ts"}}, +{"pid":1,"tid":758077059,"ph":"B","cat":"parse","ts":55,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/epsilon.d.ts"}}, +{"pid":1,"tid":758077059,"ph":"E","cat":"parse","ts":56,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/epsilon.d.ts"}}, +{"pid":1,"tid":424664868,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/delta.d.ts"}}, +{"pid":1,"tid":424664868,"ph":"B","cat":"parse","ts":57,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/delta.d.ts"}}, +{"pid":1,"tid":424664868,"ph":"E","cat":"parse","ts":58,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/delta.d.ts"}}, +{"pid":1,"tid":562550315,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/gamma.d.ts"}}, +{"pid":1,"tid":562550315,"ph":"B","cat":"parse","ts":59,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/gamma.d.ts"}}, +{"pid":1,"tid":562550315,"ph":"E","cat":"parse","ts":60,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/gamma.d.ts"}}, +{"pid":1,"tid":212488040,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/beta.d.ts"}}, +{"pid":1,"tid":212488040,"ph":"B","cat":"parse","ts":61,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/beta.d.ts"}}, +{"pid":1,"tid":212488040,"ph":"E","cat":"parse","ts":62,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/beta.d.ts"}}, +{"pid":1,"tid":180310947,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"file:/home/src/workspaces/project/parts/alpha.d.ts"}}, +{"pid":1,"tid":180310947,"ph":"B","cat":"parse","ts":63,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/alpha.d.ts"}}, +{"pid":1,"tid":180310947,"ph":"E","cat":"parse","ts":64,"name":"createSourceFile","args":{"path":"/home/src/workspaces/project/parts/alpha.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"program","ts":65,"name":"createProgram","args":{"configFilePath":"/home/src/workspaces/project/tsconfig.json"}}, +{"pid":1,"tid":1,"ph":"B","cat":"bind","ts":66,"name":"bindSourceFiles"}, +{"pid":1,"tid":373807619,"ph":"B","cat":"bind","ts":67,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/index.d.ts"}}, +{"pid":1,"tid":373807619,"ph":"E","cat":"bind","ts":68,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/index.d.ts"}}, +{"pid":1,"tid":872471718,"ph":"B","cat":"bind","ts":69,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/pt.d.ts"}}, +{"pid":1,"tid":872471718,"ph":"E","cat":"bind","ts":70,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/pt.d.ts"}}, +{"pid":1,"tid":944314902,"ph":"B","cat":"bind","ts":71,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/pl.d.ts"}}, +{"pid":1,"tid":944314902,"ph":"E","cat":"bind","ts":72,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/pl.d.ts"}}, +{"pid":1,"tid":255493617,"ph":"B","cat":"bind","ts":73,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/nl.d.ts"}}, +{"pid":1,"tid":255493617,"ph":"E","cat":"bind","ts":74,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/nl.d.ts"}}, +{"pid":1,"tid":376655012,"ph":"B","cat":"bind","ts":75,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ko.d.ts"}}, +{"pid":1,"tid":376655012,"ph":"E","cat":"bind","ts":76,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ko.d.ts"}}, +{"pid":1,"tid":345504644,"ph":"B","cat":"bind","ts":77,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ja.d.ts"}}, +{"pid":1,"tid":345504644,"ph":"E","cat":"bind","ts":78,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ja.d.ts"}}, +{"pid":1,"tid":450563598,"ph":"B","cat":"bind","ts":79,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/it.d.ts"}}, +{"pid":1,"tid":450563598,"ph":"E","cat":"bind","ts":80,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/it.d.ts"}}, +{"pid":1,"tid":141527137,"ph":"B","cat":"bind","ts":81,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/he.d.ts"}}, +{"pid":1,"tid":141527137,"ph":"E","cat":"bind","ts":82,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/he.d.ts"}}, +{"pid":1,"tid":883991556,"ph":"B","cat":"bind","ts":83,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/fr.d.ts"}}, +{"pid":1,"tid":883991556,"ph":"E","cat":"bind","ts":84,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/fr.d.ts"}}, +{"pid":1,"tid":455027228,"ph":"B","cat":"bind","ts":85,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/es.d.ts"}}, +{"pid":1,"tid":455027228,"ph":"E","cat":"bind","ts":86,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/es.d.ts"}}, +{"pid":1,"tid":616565165,"ph":"B","cat":"bind","ts":87,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/en.d.ts"}}, +{"pid":1,"tid":616565165,"ph":"E","cat":"bind","ts":88,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/en.d.ts"}}, +{"pid":1,"tid":930019567,"ph":"B","cat":"bind","ts":89,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/de.d.ts"}}, +{"pid":1,"tid":930019567,"ph":"E","cat":"bind","ts":90,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/de.d.ts"}}, +{"pid":1,"tid":743285103,"ph":"B","cat":"bind","ts":91,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ar.d.ts"}}, +{"pid":1,"tid":743285103,"ph":"E","cat":"bind","ts":92,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/locales/ar.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"B","cat":"bind","ts":93,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"bind","ts":94,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":540121770,"ph":"B","cat":"bind","ts":95,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/widget.d.ts"}}, +{"pid":1,"tid":540121770,"ph":"E","cat":"bind","ts":96,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/widget.d.ts"}}, +{"pid":1,"tid":411501950,"ph":"B","cat":"bind","ts":97,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/gadget.d.ts"}}, +{"pid":1,"tid":411501950,"ph":"E","cat":"bind","ts":98,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/gadget.d.ts"}}, +{"pid":1,"tid":454685436,"ph":"B","cat":"bind","ts":99,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/fields.d.ts"}}, +{"pid":1,"tid":454685436,"ph":"E","cat":"bind","ts":100,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/fields.d.ts"}}, +{"pid":1,"tid":911467565,"ph":"B","cat":"bind","ts":101,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/index.d.ts"}}, +{"pid":1,"tid":911467565,"ph":"E","cat":"bind","ts":102,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/index.d.ts"}}, +{"pid":1,"tid":3296854,"ph":"B","cat":"bind","ts":103,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/mu.d.ts"}}, +{"pid":1,"tid":3296854,"ph":"E","cat":"bind","ts":104,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/mu.d.ts"}}, +{"pid":1,"tid":172708103,"ph":"B","cat":"bind","ts":105,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/lambda.d.ts"}}, +{"pid":1,"tid":172708103,"ph":"E","cat":"bind","ts":106,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/lambda.d.ts"}}, +{"pid":1,"tid":887942961,"ph":"B","cat":"bind","ts":107,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/kappa.d.ts"}}, +{"pid":1,"tid":887942961,"ph":"E","cat":"bind","ts":108,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/kappa.d.ts"}}, +{"pid":1,"tid":757955809,"ph":"B","cat":"bind","ts":109,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/iota.d.ts"}}, +{"pid":1,"tid":757955809,"ph":"E","cat":"bind","ts":110,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/iota.d.ts"}}, +{"pid":1,"tid":909662959,"ph":"B","cat":"bind","ts":111,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/theta.d.ts"}}, +{"pid":1,"tid":909662959,"ph":"E","cat":"bind","ts":112,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/theta.d.ts"}}, +{"pid":1,"tid":86181510,"ph":"B","cat":"bind","ts":113,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/eta.d.ts"}}, +{"pid":1,"tid":86181510,"ph":"E","cat":"bind","ts":114,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/eta.d.ts"}}, +{"pid":1,"tid":813403241,"ph":"B","cat":"bind","ts":115,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/zeta.d.ts"}}, +{"pid":1,"tid":813403241,"ph":"E","cat":"bind","ts":116,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/zeta.d.ts"}}, +{"pid":1,"tid":758077059,"ph":"B","cat":"bind","ts":117,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/epsilon.d.ts"}}, +{"pid":1,"tid":758077059,"ph":"E","cat":"bind","ts":118,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/epsilon.d.ts"}}, +{"pid":1,"tid":424664868,"ph":"B","cat":"bind","ts":119,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/delta.d.ts"}}, +{"pid":1,"tid":424664868,"ph":"E","cat":"bind","ts":120,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/delta.d.ts"}}, +{"pid":1,"tid":562550315,"ph":"B","cat":"bind","ts":121,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/gamma.d.ts"}}, +{"pid":1,"tid":562550315,"ph":"E","cat":"bind","ts":122,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/gamma.d.ts"}}, +{"pid":1,"tid":212488040,"ph":"B","cat":"bind","ts":123,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/beta.d.ts"}}, +{"pid":1,"tid":212488040,"ph":"E","cat":"bind","ts":124,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/beta.d.ts"}}, +{"pid":1,"tid":180310947,"ph":"B","cat":"bind","ts":125,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/alpha.d.ts"}}, +{"pid":1,"tid":180310947,"ph":"E","cat":"bind","ts":126,"name":"bindSourceFile","args":{"path":"/home/src/workspaces/project/parts/alpha.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"B","cat":"bind","ts":127,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":181085097,"ph":"E","cat":"bind","ts":128,"name":"bindSourceFile","args":{"path":"/home/src/tslibs/TS/Lib/lib.es2026.full.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"bind","ts":129,"name":"bindSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"check","ts":130,"name":"checkSourceFiles"}, +{"pid":1,"tid":2,"ph":"M","cat":"__metadata","ts":1,"name":"thread_name","args":{"name":"checker:0"}}, +{"pid":1,"tid":2,"ph":"B","cat":"check","ts":131,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":2,"ph":"E","cat":"check","ts":132,"name":"checkSourceFile","args":{"checkerId":0,"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"check","ts":133,"name":"checkSourceFiles"}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":134,"name":"emit"}, +{"pid":1,"tid":100209852,"ph":"B","cat":"emit","ts":135,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"B","cat":"emit","ts":136,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":137,"name":"emitDeclarationFileOrBundle","args":{"declarationFilePath":"/home/src/workspaces/project/out/main.d.ts"}}, +{"pid":1,"tid":100209852,"ph":"E","cat":"emit","ts":138,"name":"emit","args":{"path":"/home/src/workspaces/project/main.ts"}}, +{"pid":1,"tid":1,"ph":"E","cat":"emit","ts":139,"name":"emit"} +] + +//// [/home/src/workspaces/project/trace/types_0.json] *new* +[{"id":1,"intrinsicName":"any","recursionId":0,"flags":["Any"]}, +{"id":2,"intrinsicName":"any","recursionId":1,"flags":["Any"]}, +{"id":3,"intrinsicName":"any","recursionId":2,"flags":["Any"]}, +{"id":4,"intrinsicName":"any","recursionId":3,"flags":["Any"]}, +{"id":5,"intrinsicName":"error","recursionId":4,"flags":["Any"]}, +{"id":6,"intrinsicName":"unresolved","recursionId":5,"flags":["Any"]}, +{"id":7,"intrinsicName":"any","recursionId":6,"flags":["Any"]}, +{"id":8,"intrinsicName":"intrinsic","recursionId":7,"flags":["Any"]}, +{"id":9,"intrinsicName":"unknown","recursionId":8,"flags":["Unknown"]}, +{"id":10,"intrinsicName":"undefined","recursionId":9,"flags":["Undefined"]}, +{"id":11,"intrinsicName":"undefined","recursionId":10,"flags":["Undefined"]}, +{"id":12,"intrinsicName":"undefined","recursionId":11,"flags":["Undefined"]}, +{"id":13,"intrinsicName":"null","recursionId":12,"flags":["Null"]}, +{"id":14,"intrinsicName":"string","recursionId":13,"flags":["String"]}, +{"id":15,"intrinsicName":"number","recursionId":14,"flags":["Number"]}, +{"id":16,"intrinsicName":"bigint","recursionId":15,"flags":["BigInt"]}, +{"id":17,"recursionId":16,"flags":["BooleanLiteral"],"display":"false"}, +{"id":18,"recursionId":17,"flags":["BooleanLiteral"],"display":"false"}, +{"id":19,"recursionId":18,"flags":["BooleanLiteral"],"display":"true"}, +{"id":20,"recursionId":19,"flags":["BooleanLiteral"],"display":"true"}, +{"id":21,"recursionId":20,"unionTypes":[17,19],"flags":["Boolean","Union"],"display":"boolean"}, +{"id":22,"intrinsicName":"symbol","recursionId":21,"flags":["ESSymbol"]}, +{"id":23,"intrinsicName":"void","recursionId":22,"flags":["Void"]}, +{"id":24,"intrinsicName":"never","recursionId":23,"flags":["Never"]}, +{"id":25,"intrinsicName":"never","recursionId":24,"flags":["Never"]}, +{"id":26,"intrinsicName":"never","recursionId":25,"flags":["Never"]}, +{"id":27,"intrinsicName":"never","recursionId":26,"flags":["Never"]}, +{"id":28,"intrinsicName":"object","recursionId":27,"flags":["NonPrimitive"]}, +{"id":29,"recursionId":28,"unionTypes":[14,15],"flags":["Union"],"display":"string | number"}, +{"id":30,"recursionId":29,"unionTypes":[14,15,22],"flags":["Union"],"display":"string | number | symbol"}, +{"id":31,"recursionId":30,"unionTypes":[15,16],"flags":["Union"],"display":"number | bigint"}, +{"id":32,"recursionId":31,"flags":["TemplateLiteral"],"display":"`${number}`"}, +{"id":33,"recursionId":32,"unionTypes":[10,13,14,15,16,17,19],"flags":["Union"],"display":"string | number | bigint | boolean | null | undefined"}, +{"id":34,"intrinsicName":"never","recursionId":33,"flags":["Never"]}, +{"id":35,"recursionId":34,"flags":["Object"],"display":"{}"}, +{"id":36,"recursionId":35,"flags":["Object"],"display":"{}"}, +{"id":37,"recursionId":36,"flags":["Object"],"display":"{}"}, +{"id":38,"symbolName":"__type","recursionId":37,"flags":["Object"],"display":"{}"}, +{"id":39,"recursionId":38,"flags":["Object"],"display":"{}"}, +{"id":40,"recursionId":39,"unionTypes":[10,13,39],"flags":["Union"],"display":"{} | null | undefined"}, +{"id":41,"recursionId":40,"flags":["Object"],"display":"{}"}, +{"id":42,"recursionId":41,"flags":["Object"],"display":"{}"}, +{"id":43,"recursionId":42,"flags":["Object"],"display":"{}"}, +{"id":44,"recursionId":43,"flags":["Object"],"display":"{}"}, +{"id":45,"recursionId":44,"flags":["Object"],"display":"{}"}, +{"id":46,"recursionId":45,"flags":["TypeParameter"]}, +{"id":47,"recursionId":46,"flags":["TypeParameter"]}, +{"id":48,"recursionId":47,"flags":["TypeParameter"]}, +{"id":49,"recursionId":48,"flags":["TypeParameter"]}, +{"id":50,"recursionId":49,"flags":["TypeParameter"]}, +{"id":51,"recursionId":50,"flags":["StringLiteral"],"display":"\"\""}, +{"id":52,"recursionId":51,"flags":["NumberLiteral"],"display":"0"}, +{"id":53,"recursionId":52,"flags":["BigIntLiteral"],"display":"0n"}, +{"id":54,"recursionId":53,"flags":["StringLiteral"],"display":"\"bigint\""}, +{"id":55,"recursionId":54,"flags":["StringLiteral"],"display":"\"boolean\""}, +{"id":56,"recursionId":55,"flags":["StringLiteral"],"display":"\"function\""}, +{"id":57,"recursionId":56,"flags":["StringLiteral"],"display":"\"number\""}, +{"id":58,"recursionId":57,"flags":["StringLiteral"],"display":"\"object\""}, +{"id":59,"recursionId":58,"flags":["StringLiteral"],"display":"\"string\""}, +{"id":60,"recursionId":59,"flags":["StringLiteral"],"display":"\"symbol\""}, +{"id":61,"recursionId":60,"flags":["StringLiteral"],"display":"\"undefined\""}, +{"id":62,"recursionId":61,"unionTypes":[54,55,56,57,58,59,60,61],"flags":["Union"],"display":"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" | \"string\" | \"symbol\" | \"undefined\""}, +{"id":63,"symbolName":"IArguments","recursionId":62,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":6,"character":1},"end":{"line":6,"character":24}},"flags":["Object"]}, +{"id":64,"symbolName":"globalThis","recursionId":63,"flags":["Object"],"display":"typeof globalThis"}, +{"id":65,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[66],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":66,"symbolName":"T","recursionId":65,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":17},"end":{"line":11,"character":18}},"flags":["TypeParameter"]}, +{"id":67,"symbolName":"Array","recursionId":64,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["TypeParameter"]}, +{"id":68,"symbolName":"Object","recursionId":66,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":8,"character":1},"end":{"line":8,"character":20}},"flags":["Object"]}, +{"id":69,"symbolName":"Function","recursionId":67,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":3,"character":1},"end":{"line":3,"character":22}},"flags":["Object"]}, +{"id":70,"symbolName":"CallableFunction","recursionId":68,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":4,"character":1},"end":{"line":4,"character":30}},"flags":["Object"]}, +{"id":71,"symbolName":"NewableFunction","recursionId":69,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":5,"character":1},"end":{"line":5,"character":29}},"flags":["Object"]}, +{"id":72,"symbolName":"String","recursionId":70,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":10,"character":1},"end":{"line":10,"character":34}},"flags":["Object"]}, +{"id":73,"symbolName":"Number","recursionId":71,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":7,"character":1},"end":{"line":7,"character":41}},"flags":["Object"]}, +{"id":74,"symbolName":"Boolean","recursionId":72,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":2,"character":1},"end":{"line":2,"character":21}},"flags":["Object"]}, +{"id":75,"symbolName":"RegExp","recursionId":73,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":9,"character":1},"end":{"line":9,"character":20}},"flags":["Object"]}, +{"id":76,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":77,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[2],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":78,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[79],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":79,"symbolName":"T","recursionId":75,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":25},"end":{"line":12,"character":26}},"flags":["TypeParameter"]}, +{"id":80,"symbolName":"ReadonlyArray","recursionId":74,"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["TypeParameter"]}, +{"id":81,"symbolName":"ReadonlyArray","recursionId":74,"instantiatedType":78,"typeArguments":[1],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":12,"character":1},"end":{"line":12,"character":30}},"flags":["Object"]}, +{"id":82,"symbolName":"\"/home/src/workspaces/project/parts/index\"","recursionId":76,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/index.d.ts","start":{"line":1,"character":1},"end":{"line":14,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/index\")"}, +{"id":83,"symbolName":"\"/home/src/workspaces/project/parts/alpha\"","recursionId":77,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/alpha.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/alpha\")"}, +{"id":84,"symbolName":"\"/home/src/workspaces/project/parts/beta\"","recursionId":78,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/beta.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/beta\")"}, +{"id":85,"symbolName":"\"/home/src/workspaces/project/parts/gamma\"","recursionId":79,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/gamma.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/gamma\")"}, +{"id":86,"symbolName":"\"/home/src/workspaces/project/parts/delta\"","recursionId":80,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/delta.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/delta\")"}, +{"id":87,"symbolName":"\"/home/src/workspaces/project/parts/epsilon\"","recursionId":81,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/epsilon.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/epsilon\")"}, +{"id":88,"symbolName":"\"/home/src/workspaces/project/parts/zeta\"","recursionId":82,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/zeta.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/zeta\")"}, +{"id":89,"symbolName":"\"/home/src/workspaces/project/parts/eta\"","recursionId":83,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/eta.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/eta\")"}, +{"id":90,"symbolName":"\"/home/src/workspaces/project/parts/theta\"","recursionId":84,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/theta.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/theta\")"}, +{"id":91,"symbolName":"\"/home/src/workspaces/project/parts/iota\"","recursionId":85,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/iota.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/iota\")"}, +{"id":92,"symbolName":"\"/home/src/workspaces/project/parts/kappa\"","recursionId":86,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/kappa.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/kappa\")"}, +{"id":93,"symbolName":"\"/home/src/workspaces/project/parts/lambda\"","recursionId":87,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/lambda.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/lambda\")"}, +{"id":94,"symbolName":"\"/home/src/workspaces/project/parts/mu\"","recursionId":88,"firstDeclaration":{"path":"/home/src/workspaces/project/parts/mu.d.ts","start":{"line":1,"character":1},"end":{"line":2,"character":1}},"flags":["Object"],"display":"typeof import(\"/home/src/workspaces/project/parts/mu\")"}, +{"id":95,"recursionId":89,"flags":["NumberLiteral"],"display":"1"}, +{"id":96,"recursionId":90,"flags":["NumberLiteral"],"display":"1"}, +{"id":97,"recursionId":91,"flags":["NumberLiteral"],"display":"2"}, +{"id":98,"recursionId":92,"flags":["NumberLiteral"],"display":"2"}, +{"id":99,"recursionId":93,"flags":["NumberLiteral"],"display":"3"}, +{"id":100,"recursionId":94,"flags":["NumberLiteral"],"display":"3"}, +{"id":101,"recursionId":95,"flags":["NumberLiteral"],"display":"4"}, +{"id":102,"recursionId":96,"flags":["NumberLiteral"],"display":"4"}, +{"id":103,"recursionId":97,"flags":["NumberLiteral"],"display":"5"}, +{"id":104,"recursionId":98,"flags":["NumberLiteral"],"display":"5"}, +{"id":105,"recursionId":99,"flags":["NumberLiteral"],"display":"6"}, +{"id":106,"recursionId":100,"flags":["NumberLiteral"],"display":"6"}, +{"id":107,"recursionId":101,"flags":["NumberLiteral"],"display":"7"}, +{"id":108,"recursionId":102,"flags":["NumberLiteral"],"display":"7"}, +{"id":109,"recursionId":103,"flags":["NumberLiteral"],"display":"8"}, +{"id":110,"recursionId":104,"flags":["NumberLiteral"],"display":"8"}, +{"id":111,"recursionId":105,"flags":["NumberLiteral"],"display":"9"}, +{"id":112,"recursionId":106,"flags":["NumberLiteral"],"display":"9"}, +{"id":113,"recursionId":107,"flags":["NumberLiteral"],"display":"10"}, +{"id":114,"recursionId":108,"flags":["NumberLiteral"],"display":"10"}, +{"id":115,"recursionId":109,"flags":["NumberLiteral"],"display":"11"}, +{"id":116,"recursionId":110,"flags":["NumberLiteral"],"display":"11"}, +{"id":117,"recursionId":111,"flags":["NumberLiteral"],"display":"12"}, +{"id":118,"recursionId":112,"flags":["NumberLiteral"],"display":"12"}, +{"id":119,"symbolName":"__type","recursionId":113,"firstDeclaration":{"path":"/home/src/workspaces/project/main.ts","start":{"line":5,"character":29},"end":{"line":5,"character":106}},"flags":["Object"],"display":"{ a: 1; b: 2; c: 3; d: 4; e: 5; f: 6; g: 7; h: 8; i: 9; j: 10; k: 11; l: 12; }"}, +{"id":120,"symbolName":"T","recursionId":114,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":20},"end":{"line":1,"character":21}},"flags":["TypeParameter"]}, +{"id":121,"symbolName":"T","recursionId":114,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":20},"end":{"line":1,"character":21}},"flags":["TypeParameter"]}, +{"id":122,"symbolName":"__type","recursionId":115,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":35},"end":{"line":1,"character":181}},"flags":["Object"],"display":"{ a: A; b: B; c: C; d: D; e: E; f: F; g: G; h: H; i: I; j: J; k: K; l: L; }"}, +{"id":123,"symbolName":"A","recursionId":116,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":46},"end":{"line":1,"character":47}},"flags":["TypeParameter"]}, +{"id":124,"symbolName":"B","recursionId":117,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":58},"end":{"line":1,"character":59}},"flags":["TypeParameter"]}, +{"id":125,"symbolName":"C","recursionId":118,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":70},"end":{"line":1,"character":71}},"flags":["TypeParameter"]}, +{"id":126,"symbolName":"D","recursionId":119,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":82},"end":{"line":1,"character":83}},"flags":["TypeParameter"]}, +{"id":127,"symbolName":"E","recursionId":120,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":94},"end":{"line":1,"character":95}},"flags":["TypeParameter"]}, +{"id":128,"symbolName":"F","recursionId":121,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":106},"end":{"line":1,"character":107}},"flags":["TypeParameter"]}, +{"id":129,"symbolName":"G","recursionId":122,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":118},"end":{"line":1,"character":119}},"flags":["TypeParameter"]}, +{"id":130,"symbolName":"H","recursionId":123,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":130},"end":{"line":1,"character":131}},"flags":["TypeParameter"]}, +{"id":131,"symbolName":"I","recursionId":124,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":142},"end":{"line":1,"character":143}},"flags":["TypeParameter"]}, +{"id":132,"symbolName":"J","recursionId":125,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":154},"end":{"line":1,"character":155}},"flags":["TypeParameter"]}, +{"id":133,"symbolName":"K","recursionId":126,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":166},"end":{"line":1,"character":167}},"flags":["TypeParameter"]}, +{"id":134,"symbolName":"L","recursionId":127,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":178},"end":{"line":1,"character":179}},"flags":["TypeParameter"]}, +{"id":135,"symbolName":"__type","recursionId":115,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":35},"end":{"line":1,"character":181}},"flags":["Object"],"display":"{ a: unknown; b: unknown; c: unknown; d: unknown; e: unknown; f: unknown; g: unknown; h: unknown; i: unknown; j: unknown; k: unknown; l: unknown; }"}, +{"id":136,"symbolName":"Fields","recursionId":128,"aliasTypeArguments":[120],"conditionalCheckType":121,"conditionalExtendsType":122,"conditionalTrueType":-1,"conditionalFalseType":-1,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":1},"end":{"line":1,"character":229}},"flags":["Conditional"]}, +{"id":137,"symbolName":"__type","recursionId":115,"firstDeclaration":{"path":"/home/src/workspaces/project/fields.d.ts","start":{"line":1,"character":35},"end":{"line":1,"character":181}},"flags":["Object"],"display":"{ a: 1; b: 2; c: 3; d: 4; e: 5; f: 6; g: 7; h: 8; i: 9; j: 10; k: 11; l: 12; }"}, +{"id":138,"recursionId":129,"flags":["TypeParameter"]}, +{"id":139,"recursionId":130,"flags":["TypeParameter"]}, +{"id":140,"recursionId":131,"flags":["TypeParameter"]}, +{"id":141,"recursionId":132,"flags":["TypeParameter"]}, +{"id":142,"recursionId":133,"flags":["TypeParameter"]}, +{"id":143,"recursionId":134,"flags":["TypeParameter"]}, +{"id":144,"recursionId":135,"flags":["TypeParameter"]}, +{"id":145,"recursionId":136,"flags":["TypeParameter"]}, +{"id":146,"recursionId":137,"flags":["TypeParameter"]}, +{"id":147,"recursionId":138,"flags":["TypeParameter"]}, +{"id":148,"recursionId":139,"flags":["TypeParameter"]}, +{"id":149,"recursionId":140,"flags":["TypeParameter"]}, +{"id":150,"recursionId":141,"isTuple":true,"instantiatedType":150,"typeArguments":[138,139,140,141,142,143,144,145,146,147,148,149],"flags":["Object"]}, +{"id":151,"recursionId":142,"flags":["TypeParameter"]}, +{"id":152,"recursionId":143,"instantiatedType":150,"typeArguments":[123,124,125,126,127,128,129,130,131,132,133,134],"flags":["Object"]}, +{"id":153,"recursionId":141,"instantiatedType":150,"typeArguments":[95,97,99,101,103,105,107,109,111,113,115,117],"flags":["Object"]}, +{"id":154,"recursionId":144,"instantiatedType":150,"typeArguments":[95,97,99,101,103,105,107,109,111,113,115,117],"flags":["Object"]}, +{"id":155,"recursionId":145,"unionTypes":[138,139,140,141,142,143,144,145,146,147,148,149],"flags":["Union"],"display":"? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ?"}, +{"id":156,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[155],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":157,"recursionId":146,"unionTypes":[95,97,99,101,103,105,107,109,111,113,115,117],"flags":["Union"],"display":"1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12"}, +{"id":158,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[157],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":159,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[157,153],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":160,"symbolName":"Array","recursionId":64,"instantiatedType":65,"typeArguments":[157,154],"firstDeclaration":{"path":"/home/src/tslibs/ts/lib/lib.es2026.full.d.ts","start":{"line":11,"character":1},"end":{"line":11,"character":55}},"flags":["Object"]}, +{"id":161,"symbolName":"makeWidget","recursionId":147,"firstDeclaration":{"path":"/home/src/workspaces/project/widget.d.ts","start":{"line":2,"character":1},"end":{"line":2,"character":46}},"flags":["Object"],"display":"() => Gadget"}, +{"id":162,"symbolName":"Gadget","recursionId":148,"firstDeclaration":{"path":"/home/src/workspaces/project/gadget.d.ts","start":{"line":1,"character":1},"end":{"line":3,"character":2}},"flags":["Object"]}, +{"id":163,"symbolName":"__type","recursionId":149,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/ar.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"ar\"; }"}, +{"id":164,"symbolName":"__type","recursionId":150,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/de.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"de\"; }"}, +{"id":165,"symbolName":"__type","recursionId":151,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/en.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"en\"; }"}, +{"id":166,"symbolName":"__type","recursionId":152,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/es.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"es\"; }"}, +{"id":167,"symbolName":"__type","recursionId":153,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/fr.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"fr\"; }"}, +{"id":168,"symbolName":"__type","recursionId":154,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/he.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"he\"; }"}, +{"id":169,"symbolName":"__type","recursionId":155,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/it.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"it\"; }"}, +{"id":170,"symbolName":"__type","recursionId":156,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/ja.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"ja\"; }"}, +{"id":171,"symbolName":"__type","recursionId":157,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/ko.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"ko\"; }"}, +{"id":172,"symbolName":"__type","recursionId":158,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/nl.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"nl\"; }"}, +{"id":173,"symbolName":"__type","recursionId":159,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/pl.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"pl\"; }"}, +{"id":174,"symbolName":"__type","recursionId":160,"firstDeclaration":{"path":"/home/src/workspaces/project/locales/pt.d.ts","start":{"line":1,"character":25},"end":{"line":1,"character":47}},"flags":["Object"],"display":"() => { locale: \"pt\"; }"}] + + diff --git a/tsc/testdata/tests/cases/compiler/augmentExportEquals2.ts b/tsc/testdata/tests/cases/compiler/augmentExportEquals2.ts index edf529992dcfb..12bb857e2658c 100644 --- a/tsc/testdata/tests/cases/compiler/augmentExportEquals2.ts +++ b/tsc/testdata/tests/cases/compiler/augmentExportEquals2.ts @@ -14,7 +14,6 @@ declare module "./file1" { interface A { a } } -// @filename: file3.ts // @filename: file3.ts import x = require("./file1"); import "./file2"; diff --git a/tsc/testdata/tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts b/tsc/testdata/tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts new file mode 100644 index 0000000000000..e068b9826487c --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.ts @@ -0,0 +1,73 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /index.d.ts +export { v as b } from "ia"; +export { v as a } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; + +// @Filename: /gadget.d.ts +export interface Gadget { id: number } + +// @Filename: /widget.d.ts +import { Gadget } from "./gadget"; +export declare function makeWidget(): Gadget; + +// @Filename: /main.ts +/// +import { f1 } from "./index"; +import { makeWidget } from "./widget"; +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const w = makeWidget(); +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts b/tsc/testdata/tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts new file mode 100644 index 0000000000000..e6f53e3d83519 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/exportStarSkipsNamedDefaultExport.ts @@ -0,0 +1,25 @@ +// @module: commonjs +// @target: es2015 +// @declaration: true + +// A module augmentation can merge a named declaration into a default export, so the +// default export's key in the module's exports is not the name of its symbol. + +// @Filename: /a.ts +interface I {} +export default I; +export interface J {} + +// @Filename: /b.ts +export {}; +declare module './a' { + export default interface I { x: number; } +} + +// @Filename: /c.ts +export * from "./a"; + +// @Filename: /d.ts +import { I, J } from "./c"; +export const i: I = { x: 0 }; +export const j: J = {}; diff --git a/tsc/testdata/tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts b/tsc/testdata/tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts new file mode 100644 index 0000000000000..9361fe9f688d0 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/namespaceMembersResolutionOrderExportStar.ts @@ -0,0 +1,68 @@ +// @strict: true +// @module: commonjs +// @target: es2020 +// @allowJs: true +// @checkJs: false +// @declaration: true +// @outDir: /out +// @noImplicitReferences: true +// @singleThreaded: true + +// @Filename: /ma.js +export { v as pa } from "./xa"; +export const ma = 1; + +// @Filename: /mb.js +export { v as pb } from "./xb"; +export const mb = 1; + +// @Filename: /xa.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /xb.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ia.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ib.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /star.js +export { v as a } from "./ia"; + +// @Filename: /index.js +export { v as b } from "./ib"; +export * from "./star"; +export const f1 = 1; +export const f2 = 2; +export const f3 = 3; +export const f4 = 4; +export const f5 = 5; +export const f6 = 6; +export const f7 = 7; +export const f8 = 8; +export const f9 = 9; +export const f10 = 10; + +// @Filename: /main.ts +import * as idx from "./index"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; diff --git a/tsc/testdata/tests/cases/compiler/narrowingNoInfer2.ts b/tsc/testdata/tests/cases/compiler/narrowingNoInfer2.ts new file mode 100644 index 0000000000000..aee756782f0e2 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/narrowingNoInfer2.ts @@ -0,0 +1,26 @@ +// @strict: true +// @noEmit: true + +type U1 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; + +function f1(u: U1) { + if (u.kind === "a") { + u.a; + } +} + +type U2 = NoInfer<{ kind: "a"; a: number } | { kind: "b"; b: number }> | { kind: "c" }; + +interface Box { + u: U2; + t: T; +} + +declare const box: Box; +box.u; + +function f2(u: U2) { + if (u.kind === "a") { + u.a; + } +} diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts new file mode 100644 index 0000000000000..89b31e8e9ea77 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.ts @@ -0,0 +1,73 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mt" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; +} +declare module "mm" { + import t = require("mt"); + export = t; + export const C: unique symbol; + global { + const gc: { c: typeof C }; + } +} + +// @Filename: /main.ts +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const w = gc; +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts new file mode 100644 index 0000000000000..3e00eb2833ede --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.ts @@ -0,0 +1,73 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /index.d.ts +export { v as a } from "ia"; +export { v as b } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; + +// @Filename: /gadget.d.ts +export interface Gadget { id: number } + +// @Filename: /widget.d.ts +import { Gadget } from "./gadget"; +export declare function makeWidget(): Gadget; + +// @Filename: /main.ts +/// +import { f1 } from "./index"; +import { makeWidget } from "./widget"; +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const w = makeWidget(); +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts new file mode 100644 index 0000000000000..5b835267e2b7d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.ts @@ -0,0 +1,66 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /main.ts +/// +declare const k: string; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export class C { + [k]: unknown; + sa = { ...(null! as typeof import("ma")) }; + sb = { ...(null! as typeof import("mb")) }; + f1 = 1; + f2 = 2; + f3 = 3; + f4 = 4; + f5 = 5; + f6 = 6; + f7 = 7; + f8 = 8; + f9 = 9; + f10 = 10; +} +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts new file mode 100644 index 0000000000000..4bacfefc46fab --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitRenamedExportOrder.ts @@ -0,0 +1,72 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /globals.d.ts +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); + +// @Filename: /lib.d.ts +interface X { x: 1 } +export { X as Y }; +export { v as a } from "ia"; +export { v as b } from "ib"; +export declare const f1: 1; +export declare const f2: 2; +export declare const f3: 3; +export declare const f4: 4; +export declare const f5: 5; +export declare const f6: 6; +export declare const f7: 7; +export declare const f8: 8; +export declare const f9: 9; +export declare const f10: 10; +export declare function getX(): X; + +// @Filename: /main.ts +/// +/// +import { getX } from "./lib"; +declare function expand(): { [K in keyof X]: X[K] }; +export const x = getX(); +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts new file mode 100644 index 0000000000000..b207295dd5723 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.ts @@ -0,0 +1,129 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mm" { + export const C: unique symbol; + global { + const gc: { c: typeof C }; + } +} + +// @Filename: /ua.d.ts +import { v } from "ia"; +export = v; +export as namespace UA; + +// @Filename: /ub.d.ts +import { v } from "ib"; +export = v; +export as namespace UB; + +// @Filename: /uf1.d.ts +declare const x: 1; +export = x; +export as namespace UF1; + +// @Filename: /uf2.d.ts +declare const x: 2; +export = x; +export as namespace UF2; + +// @Filename: /uf3.d.ts +declare const x: 3; +export = x; +export as namespace UF3; + +// @Filename: /uf4.d.ts +declare const x: 4; +export = x; +export as namespace UF4; + +// @Filename: /uf5.d.ts +declare const x: 5; +export = x; +export as namespace UF5; + +// @Filename: /uf6.d.ts +declare const x: 6; +export = x; +export as namespace UF6; + +// @Filename: /uf7.d.ts +declare const x: 7; +export = x; +export as namespace UF7; + +// @Filename: /uf8.d.ts +declare const x: 8; +export = x; +export as namespace UF8; + +// @Filename: /uf9.d.ts +declare const x: 9; +export = x; +export as namespace UF9; + +// @Filename: /uf10.d.ts +declare const x: 10; +export = x; +export as namespace UF10; + +// @Filename: /main.ts +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +const w = gc; +const obsA = expand(); +const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts new file mode 100644 index 0000000000000..543e04e6a4f81 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.ts @@ -0,0 +1,50 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /globals.d.ts +declare const gb: { [K in keyof typeof import("ma")]: 0 }; +declare const ga: { [K in keyof typeof import("mb")]: 0 }; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); + +// @Filename: /keys.d.ts +interface Keys { readonly key: unique symbol } +declare const keys: Keys; +declare function mk(): { [keys.key]: 1 }; + +// @Filename: /main.ts +/// +/// +/// +declare function expand(): { [K in keyof X]: X[K] }; +export const obj = mk(); +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts new file mode 100644 index 0000000000000..2ce68020ef7ab --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType1.ts @@ -0,0 +1,4 @@ +// @strict: true +// @declaration: true +class Base {} +export class C extends Base<{ e: ({ a: boolean }) => void }> {} diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts new file mode 100644 index 0000000000000..351cf83870b2a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsBindingPatternInFunctionType2.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @strict: true +// @declaration: true +// @outDir: /out +// @Filename: /a.js +/** @template T */ +class Base {} +/** @extends {Base<{ e: function({ a: boolean }): void }>} */ +export class C extends Base {} diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarCollisionOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarCollisionOrder.ts new file mode 100644 index 0000000000000..5865d51dd4ebe --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarCollisionOrder.ts @@ -0,0 +1,82 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @noEmit: true +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "s1" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; +} +declare module "s2" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; +} +declare module "m1" { + export * from "s1"; + export * from "s2"; +} + +// @Filename: /main.ts +/// +import { f1 } from "m1"; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const obsA: never = expand(); +export const obsB: never = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.ts new file mode 100644 index 0000000000000..886b93754bda6 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.ts @@ -0,0 +1,82 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @noEmit: true +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "s1" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; +} +declare module "s2" { + export const a: 1; + export const b: 2; + export const g1: 1; + export const g2: 2; + export const g3: 3; + export const g4: 4; + export const g5: 5; + export const g6: 6; + export const g7: 7; + export const g8: 8; + export const g9: 9; + export const g10: 10; +} +declare module "m1" { + export * from "s1"; + export * from "s2"; +} + +// @Filename: /main.ts +/// +import { f1 } from "m1"; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = f1; +export const obsA: never = expand(); +export const obsB: never = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts new file mode 100644 index 0000000000000..337c7236fb5a8 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.ts @@ -0,0 +1,65 @@ +// @strict: true +// @module: commonjs +// @target: es2020 +// @allowJs: true +// @checkJs: false +// @declaration: true +// @outDir: /out +// @noImplicitReferences: true +// @singleThreaded: true + +// @Filename: /ma.js +export { v as pa } from "./xa"; +export const ma = 1; + +// @Filename: /mb.js +export { v as pb } from "./xb"; +export const mb = 1; + +// @Filename: /xa.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /xb.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ia.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ib.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /index.js +export { v as a } from "./ia"; +export { v as b } from "./ib"; +export const f1 = 1; +export const f2 = 2; +export const f3 = 3; +export const f4 = 4; +export const f5 = 5; +export const f6 = 6; +export const f7 = 7; +export const f8 = 8; +export const f9 = 9; +export const f10 = 10; + +// @Filename: /main.ts +import * as idx from "./index"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts new file mode 100644 index 0000000000000..604156e3e4433 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.ts @@ -0,0 +1,82 @@ +// @strict: true +// @module: commonjs +// @target: es2020 +// @allowJs: true +// @checkJs: false +// @skipLibCheck: true +// @declaration: true +// @outDir: /out +// @noImplicitReferences: true +// @singleThreaded: true + +// @Filename: /ma.js +export { v as pa } from "./xa"; +export const ma = 1; + +// @Filename: /mb.js +export { v as pb } from "./xb"; +export const mb = 1; + +// @Filename: /xa.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /xb.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ia.js +/** @type {typeof import("./ma")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ib.js +/** @type {typeof import("./mb")} */ +const M = /** @type {any} */ (null); +const o = { v: { ...M } }; +module.exports = o; + +// @Filename: /ia2.d.ts +export { v } from "./ia"; +export type v = number; + +// @Filename: /ib2.d.ts +export { v } from "./ib"; +export type v = number; + +// @Filename: /index.d.ts +export { v as a } from "./ia2"; +export { v as b } from "./ib2"; +export const f1: 1; +export const f2: 2; +export const f3: 3; +export const f4: 4; +export const f5: 5; +export const f6: 6; +export const f7: 7; +export const f8: 8; +export const f9: 9; +export const f10: 10; + +// @Filename: /index2.d.ts +export { v as a } from "./ia2"; +export { v as b } from "./ib2"; + +// @Filename: /d.d.ts +export * from "./index"; +export * from "./index2"; + +// @Filename: /main.ts +import * as idx from "./d"; +const all = { ...idx }; +import * as MA from "./ma"; +import * as MB from "./mb"; +export const fromA = MA.pa; +export const fromB = MB.pb; +export const checkA: 1 = MA.pa.mb; +export const checkB: 1 = MB.pb.ma; diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNonExportedMemberOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNonExportedMemberOrder.ts new file mode 100644 index 0000000000000..13baed3b85905 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsNonExportedMemberOrder.ts @@ -0,0 +1,64 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @noEmit: true +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mt" { + export { v as a } from "ia"; + export { v as b } from "ib"; + export const f1: 1; + export const f2: 2; + export const f3: 3; + export const f4: 4; + export const f5: 5; + export const f6: 6; + export const f7: 7; + export const f8: 8; + export const f9: 9; + export const f10: 10; + const zz: 1; +} + +// @Filename: /main.ts +/// +import { zz } from "mt"; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const obsA: never = expand(); +export const obsB: never = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.ts new file mode 100644 index 0000000000000..1c742585da63a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyDiagnosticsSpellingSuggestionOrder.ts @@ -0,0 +1,121 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @noEmit: true +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /ua.d.ts +import { v } from "ia"; +export = v; +export as namespace UA; + +// @Filename: /ub.d.ts +import { v } from "ib"; +export = v; +export as namespace UB; + +// @Filename: /uf1.d.ts +declare const x: 1; +export = x; +export as namespace UF1; + +// @Filename: /uf2.d.ts +declare const x: 2; +export = x; +export as namespace UF2; + +// @Filename: /uf3.d.ts +declare const x: 3; +export = x; +export as namespace UF3; + +// @Filename: /uf4.d.ts +declare const x: 4; +export = x; +export as namespace UF4; + +// @Filename: /uf5.d.ts +declare const x: 5; +export = x; +export as namespace UF5; + +// @Filename: /uf6.d.ts +declare const x: 6; +export = x; +export as namespace UF6; + +// @Filename: /uf7.d.ts +declare const x: 7; +export = x; +export as namespace UF7; + +// @Filename: /uf8.d.ts +declare const x: 8; +export = x; +export as namespace UF8; + +// @Filename: /uf9.d.ts +declare const x: 9; +export = x; +export as namespace UF9; + +// @Filename: /uf10.d.ts +declare const x: 10; +export = x; +export as namespace UF10; + +// @Filename: /main.ts +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +const q = zz; +const obsA: never = expand(); +const obsB: never = expand(); diff --git a/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.ts new file mode 100644 index 0000000000000..7e31032b5522c --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.ts @@ -0,0 +1,61 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @noEmit: true +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} + +// @Filename: /mm.ts +/// +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare const o: { x: 1 }; +export = o; +export { v as a } from "ia"; +export { v as b } from "ib"; +export interface F1 {} +export interface F2 {} +export interface F3 {} +export interface F4 {} +export interface F5 {} +export interface F6 {} +export interface F7 {} +export interface F8 {} +export interface F9 {} +export interface F10 {} +export const checkA: 0 = oxa.pb; +export const checkB: 0 = oxb.pa; diff --git a/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts b/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts new file mode 100644 index 0000000000000..32586d0fc9125 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.ts @@ -0,0 +1,68 @@ +// @strict: true +// @skipLibCheck: true +// @module: commonjs +// @target: es2020 +// @declaration: true +// @emitDeclarationOnly: true +// @outDir: /out +// @noImplicitReferences: true + +// @Filename: /ambient.d.ts +declare module "ma" { + export { v as pa } from "xa"; + export const ma: 1; +} +declare module "mb" { + export { v as pb } from "xb"; + export const mb: 1; +} +declare module "xa" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "xb" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ia" { + type T = typeof import("ma"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "ib" { + type T = typeof import("mb"); + interface O extends T { v: 0 } + const o: O; + export = o; +} +declare module "mm" { + const o: { x: 1 }; + export = o; + export { v as a } from "ia"; + export { v as b } from "ib"; + export interface F1 {} + export interface F2 {} + export interface F3 {} + export interface F4 {} + export interface F5 {} + export interface F6 {} + export interface F7 {} + export interface F8 {} + export interface F9 {} + export interface F10 {} +} + +// @Filename: /main.ts +/// +import { x } from "mm"; +declare const oxa: typeof import("xa"); +declare const oxb: typeof import("xb"); +declare function expand(): { [K in keyof X]: X[K] }; +export const keep = x; +export const obsA = expand(); +export const obsB = expand(); diff --git a/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedEmptyModule.ts b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedEmptyModule.ts new file mode 100644 index 0000000000000..b7c9cbc02c495 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedEmptyModule.ts @@ -0,0 +1,52 @@ +// @strict: true +// @module: esnext +// @moduleResolution: bundler +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: /tsconfig.json +{ + "compilerOptions": { + "paths": { "x.svelte": ["./stub.d.ts"] }, + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] +} + +// @filename: /ambient.d.ts +declare module "*.svelte" { + const value: number; + export default value; +} +declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; +} +declare module "*.js" { + const value: number; + export default value; +} + +// @filename: /stub.d.ts +export {}; + +// @filename: /generated/component.svelte.d.ts +export {}; + +// @filename: /src/script.ts +export {}; + +// @filename: /src/main.ts +import fromPaths from "x.svelte"; +import emptyAttributes from "x.svelte" with {}; +import fromRootDirs from "./component.svelte"; +import fromRelative from "./script.js"; +const fromPathsValue: number = fromPaths; +const emptyAttributesValue: number = emptyAttributes; +const fromRootDirsValue: number = fromRootDirs; + +import text from "x.svelte" with { type: "text" }; +const textValue: string = text; + +import missing from "./missing.svelte"; +const missingValue: number = missing; diff --git a/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScript.ts b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScript.ts new file mode 100644 index 0000000000000..782e943c07625 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScript.ts @@ -0,0 +1,50 @@ +// @strict: true +// @module: esnext +// @moduleResolution: bundler +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: /tsconfig.json +{ + "compilerOptions": { + "paths": { "x.svelte": ["./stub.d.ts"] }, + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] +} + +// @filename: /ambient.d.ts +declare module "*.svelte" { + const value: number; + export default value; +} +declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; +} +declare module "*.js" { + const value: number; + export default value; +} + +// @filename: /stub.d.ts +// A resolved script must not fall back to a pattern ambient module without attributes. + +// @filename: /generated/component.svelte.d.ts +// A script resolved through rootDirs. + +// @filename: /src/script.ts +// A script resolved through a relative import. + +// @filename: /src/main.ts +import fromPaths from "x.svelte"; +import emptyAttributes from "x.svelte" with {}; +import fromRootDirs from "./component.svelte"; +import fromRelative from "./script.js"; +const fromPathsValue: string = fromPaths; + +import text from "x.svelte" with { type: "text" }; +const textValue: string = text; + +import missing from "./missing.svelte"; +const missingValue: number = missing; diff --git a/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScriptWithoutPaths.ts b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScriptWithoutPaths.ts new file mode 100644 index 0000000000000..232d40cd9f3e8 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/patternAmbientModuleResolvedScriptWithoutPaths.ts @@ -0,0 +1,49 @@ +// @strict: true +// @module: esnext +// @moduleResolution: bundler +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: /tsconfig.json +{ + "compilerOptions": { + "rootDirs": ["./src", "./generated"] + }, + "files": ["ambient.d.ts", "src/main.ts"] +} + +// @filename: /ambient.d.ts +declare module "*.svelte" { + const value: number; + export default value; +} +declare module "*.svelte" with { type: "text" } { + const value: string; + export default value; +} +declare module "*.js" { + const value: number; + export default value; +} + +// @filename: /stub.d.ts +// This file is not resolved without a paths mapping. + +// @filename: /generated/component.svelte.d.ts +// A script resolved through rootDirs. + +// @filename: /src/script.ts +// A script resolved through a relative import. + +// @filename: /src/main.ts +import fromPaths from "x.svelte"; +import emptyAttributes from "x.svelte" with {}; +import fromRootDirs from "./component.svelte"; +import fromRelative from "./script.js"; +const fromPathsValue: string = fromPaths; + +import text from "x.svelte" with { type: "text" }; +const textValue: string = text; + +import missing from "./missing.svelte"; +const missingValue: number = missing; diff --git a/tsc/testdata/tests/cases/compiler/reverseMappedTypePrivateMembers.ts b/tsc/testdata/tests/cases/compiler/reverseMappedTypePrivateMembers.ts new file mode 100644 index 0000000000000..adccf319dbcc6 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/reverseMappedTypePrivateMembers.ts @@ -0,0 +1,20 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/64593 + +class Entity { + private secret = 1; + protected hidden = 2; + markChanged(field: string): void {} +} +class Account extends Entity { + emailAddress = ''; +} + +declare function getAccount(): Readonly; +declare function getAccountPlain(): Readonly; + +export const plain = getAccountPlain() as Account; +export const explicit = getAccount() as Account; +export const inferred = getAccount() as Account; diff --git a/tsc/testdata/tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts b/tsc/testdata/tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts new file mode 100644 index 0000000000000..e625577d740fd --- /dev/null +++ b/tsc/testdata/tests/cases/conformance/internalModules/importDeclarations/importAliasMergedNamespace.ts @@ -0,0 +1,42 @@ +// @strict: true +// @target: es2022 +// @module: commonjs + +namespace N { + export namespace M { + export class C {} + } + + import Local = M.C; + new Local(); +} + +namespace N { + import A = M.C; + new A(); + + import B = M; + new B.C(); + + export import D = M.C; + new D(); + + export import E = M; + new E.C(); + + import Explicit = N.M.C; + new Explicit(); +} + +namespace Outer { + export namespace Inner { + export namespace M { + export class C {} + } + } +} + +namespace Outer.Inner { + import A = M.C; + new A(); +}