package compiler

import (
	"context"
	"fmt"
	"io"
	"maps"
	"slices"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/microsoft/typescript-go/internal/ast"
	"github.com/microsoft/typescript-go/internal/binder"
	"github.com/microsoft/typescript-go/internal/checker"
	"github.com/microsoft/typescript-go/internal/collections"
	"github.com/microsoft/typescript-go/internal/core"
	"github.com/microsoft/typescript-go/internal/diagnostics"
	"github.com/microsoft/typescript-go/internal/json"
	"github.com/microsoft/typescript-go/internal/locale"
	"github.com/microsoft/typescript-go/internal/module"
	"github.com/microsoft/typescript-go/internal/modulespecifiers"
	"github.com/microsoft/typescript-go/internal/outputpaths"
	"github.com/microsoft/typescript-go/internal/packagejson"
	"github.com/microsoft/typescript-go/internal/parser"
	"github.com/microsoft/typescript-go/internal/printer"
	"github.com/microsoft/typescript-go/internal/scanner"
	"github.com/microsoft/typescript-go/internal/sourcemap"
	"github.com/microsoft/typescript-go/internal/symlinks"
	"github.com/microsoft/typescript-go/internal/tracing"
	"github.com/microsoft/typescript-go/internal/tsoptions"
	"github.com/microsoft/typescript-go/internal/tspath"
)

type ProgramOptions struct {
	Host                        CompilerHost
	Config                      *tsoptions.ParsedCommandLine
	UseSourceOfProjectReference bool
	SingleThreaded              core.Tristate
	CreateCheckerPool           func(*Program) CheckerPool
	TypingsLocation             string
	ProjectName                 string
	Tracing                     *tracing.Tracing
}

func (p *ProgramOptions) canUseProjectReferenceSource() bool {
	return p.UseSourceOfProjectReference && !p.Config.CompilerOptions().DisableSourceOfProjectReferenceRedirect.IsTrue()
}

type lazyValue[T any] struct {
	value       *T
	once        sync.Once
	initialized atomic.Bool
}

func (l *lazyValue[T]) getValue(compute func() *T) *T {
	l.once.Do(func() {
		if l.value == nil {
			l.value = compute()
		}
		l.initialized.Store(true)
	})
	return l.value
}

func (l *lazyValue[T]) tryReuse(from *lazyValue[T]) {
	if from.initialized.Load() {
		l.value = from.value
		l.initialized.Store(true)
	}
}

type packageNamesInfo struct {
	resolved           *collections.Set[string]
	unresolved         *collections.Set[string]
	deepImportPackages *collections.Set[string]
}

type Program struct {
	opts        ProgramOptions
	checkerPool CheckerPool // always set; used as fallback for project system pools

	// compilerCheckerPool is set only when the built-in compiler checker pool is in use
	// (i.e. CreateCheckerPool was not provided). It enables grouped parallel iteration,
	// non-exclusive access for emit, and direct global diagnostics collection.
	compilerCheckerPool *checkerPool

	comparePathsOptions tspath.ComparePathsOptions

	processedFiles

	usesUriStyleNodeCoreModules core.Tristate

	commonSourceDirectory     string
	commonSourceDirectoryOnce sync.Once

	declarationDiagnosticCache collections.SyncMap[*ast.SourceFile, []*ast.Diagnostic]

	programDiagnostics         []*ast.Diagnostic
	hasEmitBlockingDiagnostics collections.Set[tspath.Path]

	sourceFilesToEmitOnce sync.Once
	sourceFilesToEmit     []*ast.SourceFile

	// Cached unresolved imports for ATA
	unresolvedImports lazyValue[collections.Set[string]]
	knownSymlinks     lazyValue[symlinks.KnownSymlinks]

	// Used by auto-imports
	packageNames lazyValue[packageNamesInfo]

	// Used by workspace/symbol
	hasTSFileOnce sync.Once
	hasTSFile     bool

	// Cached map of package names to whether they bundle types
	packagesMapOnce sync.Once
	packagesMap     map[string]bool
}

// FileExists implements checker.Program.
func (p *Program) FileExists(path string) bool {
	return p.Host().FS().FileExists(path)
}

// GetCurrentDirectory implements checker.Program.
func (p *Program) GetCurrentDirectory() string {
	return p.Host().GetCurrentDirectory()
}

// GetGlobalTypingsCacheLocation implements checker.Program.
func (p *Program) GetGlobalTypingsCacheLocation() string {
	return p.opts.TypingsLocation
}

// GetNearestAncestorDirectoryWithPackageJson implements checker.Program.
func (p *Program) GetNearestAncestorDirectoryWithPackageJson(dirname string) string {
	scoped := p.resolver.GetPackageScopeForPath(dirname)
	if scoped != nil && scoped.Exists() {
		return scoped.PackageDirectory
	}
	return ""
}

// GetPackageJsonInfo implements checker.Program.
func (p *Program) GetPackageJsonInfo(pkgJsonPath string) *packagejson.InfoCacheEntry {
	directory := tspath.GetDirectoryPath(pkgJsonPath)
	scoped := p.resolver.GetPackageScopeForPath(directory)
	if scoped != nil && scoped.Exists() && scoped.PackageDirectory == directory {
		return scoped
	}
	return nil
}

// PackageJsonCacheEntries iterates on all package json cache entries.
func (p *Program) PackageJsonCacheEntries(f func(key tspath.Path, value *packagejson.InfoCacheEntry) bool) {
	p.resolver.PackageJsonCacheEntries(f)
}

// GetRedirectTargets returns the list of file paths that redirect to the given path.
// These are files from the same package (same name@version) installed in different locations.
func (p *Program) GetRedirectTargets(path tspath.Path) []string {
	return p.redirectTargetsMap[path]
}

// gets the original file that was included in program
// this returns original source file name when including output of project reference
// otherwise same name
// Equivalent to originalFileName on SourceFile in Strada
func (p *Program) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string {
	if source, ok := p.outputFileToProjectReferenceSource[file.Path()]; ok {
		return source
	}
	return file.FileName()
}

// GetProjectReferenceFromSource implements checker.Program.
func (p *Program) GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
	return p.projectReferenceFileMapper.getProjectReferenceFromSource(path)
}

// IsSourceFromProjectReference implements checker.Program.
func (p *Program) IsSourceFromProjectReference(path tspath.Path) bool {
	return p.projectReferenceFileMapper.isSourceFromProjectReference(path)
}

func (p *Program) GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
	return p.projectReferenceFileMapper.getProjectReferenceFromOutputDts(path)
}

func (p *Program) GetResolvedProjectReferenceFor(path tspath.Path) (*tsoptions.ParsedCommandLine, bool) {
	return p.projectReferenceFileMapper.getResolvedReferenceFor(path)
}

func (p *Program) GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine {
	redirect, _ := p.projectReferenceFileMapper.getRedirectForResolution(file)
	return redirect
}

func (p *Program) GetParseFileRedirect(fileName string) string {
	return p.projectReferenceFileMapper.getParseFileRedirect(ast.NewHasFileName(fileName, p.toPath(fileName)))
}

func (p *Program) GetResolvedProjectReferences() []*tsoptions.ParsedCommandLine {
	return p.projectReferenceFileMapper.getResolvedProjectReferences()
}

func (p *Program) RangeResolvedProjectReference(f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool) bool {
	return p.projectReferenceFileMapper.rangeResolvedProjectReference(f)
}

func (p *Program) RangeResolvedProjectReferenceInChildConfig(
	childConfig *tsoptions.ParsedCommandLine,
	f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool,
) bool {
	return p.projectReferenceFileMapper.rangeResolvedProjectReferenceInChildConfig(childConfig, f)
}

// UseCaseSensitiveFileNames implements checker.Program.
func (p *Program) UseCaseSensitiveFileNames() bool {
	return p.Host().FS().UseCaseSensitiveFileNames()
}

func (p *Program) UsesUriStyleNodeCoreModules() core.Tristate {
	return p.usesUriStyleNodeCoreModules
}

var _ checker.Program = (*Program)(nil)

/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
func (p *Program) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile {
	// TODO: The module loader in corsa is fairly different than strada, it should probably be able to expose this functionality at some point,
	// rather than redoing the logic approximately here, since most of the related logic now lives in module.Resolver
	// Still, without the failed lookup reporting that only the loader does, this isn't terribly complicated

	fileName := tspath.ResolvePath(tspath.GetDirectoryPath(origin.FileName()), ref.FileName)
	supportedExtensionsBase := tsoptions.GetSupportedExtensions(p.Options(), nil /*extraFileExtensions*/)
	supportedExtensions := tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(p.Options(), supportedExtensionsBase)
	allowNonTsExtensions := p.Options().AllowNonTsExtensions.IsTrue()
	if tspath.HasExtension(fileName) {
		if !allowNonTsExtensions {
			canonicalFileName := tspath.GetCanonicalFileName(fileName, p.UseCaseSensitiveFileNames())
			supported := false
			for _, group := range supportedExtensions {
				if tspath.FileExtensionIsOneOf(canonicalFileName, group) {
					supported = true
					break
				}
			}
			if !supported {
				return nil // unsupported extensions are forced to fail
			}
		}

		return p.GetSourceFileForResolvedModule(fileName)
	}
	if allowNonTsExtensions {
		extensionless := p.GetSourceFileForResolvedModule(fileName)
		if extensionless != nil {
			return extensionless
		}
	}

	// Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts)
	for _, ext := range supportedExtensions[0] {
		result := p.GetSourceFileForResolvedModule(fileName + ext)
		if result != nil {
			return result
		}
	}
	return nil
}

func NewProgram(opts ProgramOptions) *Program {
	p := &Program{opts: opts}
	if p.opts.Tracing != nil {
		defer p.opts.Tracing.Push(tracing.PhaseProgram, "createProgram", map[string]any{"configFilePath": opts.Config.CompilerOptions().ConfigFilePath}, true)()
	}
	p.processedFiles = processAllProgramFiles(p.opts, p.SingleThreaded())
	p.initCheckerPool()
	p.verifyCompilerOptions()
	return p
}

// Return an updated program for which it is known that only the file with the given path has changed.
// In addition to a new program, return a boolean indicating whether the data of the old program was reused.
// createCheckerPool, if non-nil, overrides the CreateCheckerPool stored in the old program's options,
// ensuring each caller uses a fresh closure and avoiding data races on captured variables.
// The returned *ast.SourceFile is the changed file as acquired through newHost; it is nil
// only if the host cannot locate the file (e.g. it was deleted). Callers that manage
// host-side parse caches must release this exact pointer when the old program could not be
// reused, since it was acquired speculatively before that decision was made.
func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHost, createCheckerPool func(*Program) CheckerPool) (*Program, *ast.SourceFile, bool) {
	newOpts := p.opts
	newOpts.Host = newHost
	if createCheckerPool != nil {
		newOpts.CreateCheckerPool = createCheckerPool
	}

	oldFile := p.filesByPath[changedFilePath]
	newFile := newHost.GetSourceFile(oldFile.ParseOptions())

	// If this file is part of a package redirect group (same package installed in multiple
	// node_modules locations), we need to rebuild the program because the redirect targets
	// might need recalculation.
	_, inRedirectFiles := p.redirectFilesByPath[changedFilePath]
	_, isRedirectTarget := p.redirectTargetsMap[changedFilePath]
	if inRedirectFiles || isRedirectTarget {
		return NewProgram(newOpts), newFile, false
	}

	if !canReplaceFileInProgram(oldFile, newFile) {
		return NewProgram(newOpts), newFile, false
	}
	if oldNeedsImportHelpers := p.importHelpersImportSpecifiers[oldFile.Path()] != nil; oldNeedsImportHelpers != p.needsImportHelpersImportSpecifier(newFile) {
		return NewProgram(newOpts), newFile, false
	}
	// TODO: reverify compiler options when config has changed?
	result := &Program{
		opts:                        newOpts,
		comparePathsOptions:         p.comparePathsOptions,
		processedFiles:              p.processedFiles,
		usesUriStyleNodeCoreModules: p.usesUriStyleNodeCoreModules,
		programDiagnostics:          p.programDiagnostics,
		hasEmitBlockingDiagnostics:  p.hasEmitBlockingDiagnostics,
	}
	result.unresolvedImports.tryReuse(&p.unresolvedImports)
	result.knownSymlinks.tryReuse(&p.knownSymlinks)
	result.packageNames.tryReuse(&p.packageNames)
	result.initCheckerPool()
	index := core.FindIndex(result.files, func(file *ast.SourceFile) bool { return file.Path() == newFile.Path() })
	result.files = slices.Clone(result.files)
	result.files[index] = newFile
	result.filesByPath = maps.Clone(result.filesByPath)
	result.filesByPath[newFile.Path()] = newFile
	updateFileIncludeProcessor(result)
	return result, newFile, true
}

func (p *Program) initCheckerPool() {
	if !p.finishedProcessing {
		panic("Program must finish processing files before initializing checker pool")
	}

	if p.opts.CreateCheckerPool != nil {
		p.checkerPool = p.opts.CreateCheckerPool(p)
	} else {
		pool := newCheckerPoolWithTracing(p, p.opts.Tracing)
		p.checkerPool = pool
		p.compilerCheckerPool = pool
	}
}

// GetCheckerPool returns the checker pool associated with this program.
func (p *Program) GetCheckerPool() CheckerPool {
	return p.checkerPool
}

func canReplaceFileInProgram(file1 *ast.SourceFile, file2 *ast.SourceFile) bool {
	return file2 != nil &&
		file1.ParseOptions() == file2.ParseOptions() &&
		file1.UsesUriStyleNodeCoreModules == file2.UsesUriStyleNodeCoreModules &&
		slices.EqualFunc(file1.Imports(), file2.Imports(), equalModuleSpecifiers) &&
		slices.EqualFunc(file1.ModuleAugmentations, file2.ModuleAugmentations, equalModuleAugmentationNames) &&
		slices.Equal(file1.AmbientModuleNames, file2.AmbientModuleNames) &&
		slices.EqualFunc(file1.ReferencedFiles, file2.ReferencedFiles, equalFileReferences) &&
		slices.EqualFunc(file1.TypeReferenceDirectives, file2.TypeReferenceDirectives, equalFileReferences) &&
		slices.EqualFunc(file1.LibReferenceDirectives, file2.LibReferenceDirectives, equalFileReferences) &&
		equalCheckJSDirectives(file1.CheckJsDirective, file2.CheckJsDirective)
}

func (p *Program) needsImportHelpersImportSpecifier(file *ast.SourceFile) bool {
	redirect, _ := p.projectReferenceFileMapper.getRedirectForResolution(file)
	optionsForFile := module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect)
	if !optionsForFile.ImportHelpers.IsTrue() {
		return false
	}
	isJavaScriptFile := ast.IsSourceFileJS(file)
	isExternalModuleFile := ast.IsExternalModule(file)
	if !isJavaScriptFile && (file.IsDeclarationFile || (!optionsForFile.GetIsolatedModules() && !isExternalModuleFile)) {
		return false
	}
	return true
}

func equalModuleSpecifiers(n1 *ast.Node, n2 *ast.Node) bool {
	return n1.Kind == n2.Kind && (!ast.IsStringLiteral(n1) || n1.Text() == n2.Text())
}

func equalModuleAugmentationNames(n1 *ast.Node, n2 *ast.Node) bool {
	return n1.Kind == n2.Kind && n1.Text() == n2.Text()
}

func equalFileReferences(f1 *ast.FileReference, f2 *ast.FileReference) bool {
	return f1.FileName == f2.FileName && f1.ResolutionMode == f2.ResolutionMode && f1.Preserve == f2.Preserve
}

func equalCheckJSDirectives(d1 *ast.CheckJsDirective, d2 *ast.CheckJsDirective) bool {
	return d1 == nil && d2 == nil || d1 != nil && d2 != nil && d1.Enabled == d2.Enabled
}

func (p *Program) SourceFiles() []*ast.SourceFile               { return p.files }
func (p *Program) DuplicateSourceFiles() []*DuplicateSourceFile { return p.duplicateSourceFiles }
func (p *Program) Options() *core.CompilerOptions               { return p.opts.Config.CompilerOptions() }
func (p *Program) CommandLine() *tsoptions.ParsedCommandLine    { return p.opts.Config }
func (p *Program) Host() CompilerHost                           { return p.opts.Host }
func (p *Program) Tracing() *tracing.Tracing                    { return p.opts.Tracing }
func (p *Program) GetConfigFileParsingDiagnostics() []*ast.Diagnostic {
	return slices.Clip(p.opts.Config.GetConfigFileParsingDiagnostics())
}

// GetUnresolvedImports returns the unresolved imports for this program.
// The result is cached and computed only once.
func (p *Program) GetUnresolvedImports() *collections.Set[string] {
	return p.unresolvedImports.getValue(p.extractUnresolvedImports)
}

func (p *Program) extractUnresolvedImports() *collections.Set[string] {
	unresolvedSet := &collections.Set[string]{}

	for _, sourceFile := range p.files {
		unresolvedImports := p.extractUnresolvedImportsFromSourceFile(sourceFile)
		for _, imp := range unresolvedImports {
			unresolvedSet.Add(imp)
		}
	}

	return unresolvedSet
}

func (p *Program) extractUnresolvedImportsFromSourceFile(file *ast.SourceFile) []string {
	var unresolvedImports []string

	resolvedModules := p.resolvedModules[file.Path()]
	for cacheKey, resolution := range resolvedModules {
		resolved := resolution.IsResolved()
		if (!resolved || !tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsWithJsonFlat)) &&
			!tspath.IsExternalModuleNameRelative(cacheKey.Name) {
			unresolvedImports = append(unresolvedImports, cacheKey.Name)
		}
	}

	return unresolvedImports
}

func (p *Program) SingleThreaded() bool {
	return p.opts.SingleThreaded.DefaultIfUnknown(p.Options().SingleThreaded).IsTrue()
}

func (p *Program) BindSourceFiles() {
	wg := core.NewWorkGroup(p.SingleThreaded())
	for _, file := range p.files {
		if !file.IsBound() {
			wg.Queue(func() {
				if p.opts.Tracing != nil {
					defer p.opts.Tracing.Push(tracing.PhaseBind, "bindSourceFile", map[string]any{"path": string(file.Path())}, true)()
				}
				binder.BindSourceFile(file)
			})
		}
	}
	wg.RunAndWait()
}

// Return the type checker associated with the program.
func (p *Program) GetTypeChecker(ctx context.Context) (*checker.Checker, func()) {
	if p.compilerCheckerPool != nil {
		return p.compilerCheckerPool.getCheckerNonExclusive()
	}
	return p.checkerPool.GetChecker(ctx, nil)
}

func (p *Program) ForEachCheckerParallel(cb func(idx int, c *checker.Checker)) {
	if p.compilerCheckerPool != nil {
		p.compilerCheckerPool.forEachCheckerParallel(cb)
	}
}

// Return a checker for the given file. We may have multiple checkers in concurrent scenarios and this
// method returns the checker that was tasked with checking the file. Note that it isn't possible to mix
// types obtained from different checkers, so only non-type data (such as diagnostics or string
// representations of types) should be obtained from checkers returned by this method.
func (p *Program) GetTypeCheckerForFile(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) {
	if p.compilerCheckerPool != nil {
		return p.compilerCheckerPool.getCheckerForFileNonExclusive(file)
	}
	return p.checkerPool.GetChecker(ctx, file)
}

// Return a checker for the given file, locked to the current thread to prevent data races from multiple threads
// accessing the same checker. The lock will be released when the `done` function is called.
func (p *Program) GetTypeCheckerForFileExclusive(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) {
	if p.compilerCheckerPool != nil {
		return p.compilerCheckerPool.getCheckerForFileExclusive(ctx, file)
	}
	return p.checkerPool.GetChecker(ctx, file)
}

func (p *Program) GetResolvedModule(file ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule {
	if resolutions, ok := p.resolvedModules[file.Path()]; ok {
		if resolved, ok := resolutions[module.ModeAwareCacheKey{Name: moduleReference, Mode: mode}]; ok {
			return resolved
		}
	}
	return nil
}

func (p *Program) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule {
	if !ast.IsStringLiteralLike(moduleSpecifier) {
		panic("moduleSpecifier must be a StringLiteralLike")
	}
	mode := p.GetModeForUsageLocation(file, moduleSpecifier)
	return p.GetResolvedModule(file, moduleSpecifier.Text(), mode)
}

func (p *Program) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] {
	return p.resolvedModules
}

// GetPackagesMap returns a lazily-cached map of package names to whether they bundle types.
// This is used by incremental diagnostic repopulation.
func (p *Program) GetPackagesMap() map[string]bool {
	p.packagesMapOnce.Do(func() {
		p.packagesMap = make(map[string]bool)
		for _, resolvedModulesInFile := range p.resolvedModules {
			for _, mod := range resolvedModulesInFile {
				if mod.PackageId.Name != "" {
					p.packagesMap[mod.PackageId.Name] = p.packagesMap[mod.PackageId.Name] || mod.Extension == tspath.ExtensionDts
				}
			}
		}
	})
	return p.packagesMap
}

// collectDiagnostics collects diagnostics from a single file or all files.
// If sourceFile is non-nil, returns diagnostics for just that file.
// If sourceFile is nil, returns diagnostics for all files in the program.
func (p *Program) collectDiagnostics(ctx context.Context, sourceFile *ast.SourceFile, concurrent bool, collect func(context.Context, *ast.SourceFile) []*ast.Diagnostic) []*ast.Diagnostic {
	var result []*ast.Diagnostic
	if sourceFile != nil {
		result = collect(ctx, sourceFile)
	} else {
		diagnostics := p.collectDiagnosticsFromFiles(ctx, p.files, concurrent, collect)
		result = slices.Concat(diagnostics...)
	}
	return SortAndDeduplicateDiagnostics(result)
}

func (p *Program) collectDiagnosticsFromFiles(ctx context.Context, sourceFiles []*ast.SourceFile, concurrent bool, collect func(context.Context, *ast.SourceFile) []*ast.Diagnostic) [][]*ast.Diagnostic {
	diagnostics := make([][]*ast.Diagnostic, len(sourceFiles))
	wg := core.NewWorkGroup(!concurrent || p.SingleThreaded())
	for i, file := range sourceFiles {
		wg.Queue(func() {
			diagnostics[i] = collect(ctx, file)
		})
	}
	wg.RunAndWait()
	return diagnostics
}

// collectCheckerDiagnostics collects diagnostics from a single file or all files,
// using a callback that receives the checker for each file. When the checker pool
// supports grouped iteration (compiler pool), files are grouped by checker and
// processed in parallel with one task per checker, reducing contention and improving
// cache locality. Otherwise, falls back to per-file concurrent collection.
func (p *Program) collectCheckerDiagnostics(ctx context.Context, sourceFile *ast.SourceFile, collect func(context.Context, *checker.Checker, *ast.SourceFile) []*ast.Diagnostic) []*ast.Diagnostic {
	if sourceFile != nil {
		if p.SkipTypeChecking(sourceFile, false) {
			return nil
		}
		c, done := p.GetTypeCheckerForFileExclusive(ctx, sourceFile)
		result := collect(ctx, c, sourceFile)
		done()
		return SortAndDeduplicateDiagnostics(result)
	}
	return SortAndDeduplicateDiagnostics(slices.Concat(p.collectCheckerDiagnosticsFromFiles(ctx, p.files, collect)...))
}

// collectCheckerDiagnosticsFromFiles collects checker diagnostics for a list of files.
func (p *Program) collectCheckerDiagnosticsFromFiles(ctx context.Context, sourceFiles []*ast.SourceFile, collect func(context.Context, *checker.Checker, *ast.SourceFile) []*ast.Diagnostic) [][]*ast.Diagnostic {
	diagnostics := make([][]*ast.Diagnostic, len(sourceFiles))
	if p.compilerCheckerPool != nil {
		p.compilerCheckerPool.forEachCheckerGroupDo(ctx, sourceFiles, p.SingleThreaded(), func(c *checker.Checker, fileIndex int, file *ast.SourceFile) {
			diagnostics[fileIndex] = collect(ctx, c, file)
		})
	} else {
		wg := core.NewWorkGroup(p.SingleThreaded())
		for i, file := range sourceFiles {
			if p.SkipTypeChecking(file, false) {
				continue
			}
			wg.Queue(func() {
				c, done := p.checkerPool.GetChecker(ctx, file)
				diagnostics[i] = collect(ctx, c, file)
				done()
			})
		}
		wg.RunAndWait()
	}
	return diagnostics
}

func (p *Program) GetSyntacticDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return p.collectDiagnostics(ctx, sourceFile, false /*concurrent*/, func(_ context.Context, file *ast.SourceFile) []*ast.Diagnostic {
		diags := core.Concatenate(file.Diagnostics(), file.JSDiagnostics())
		// For JS files that won't be checked by the checker (no checkJs/ts-check), we need
		// program-level syntactic checks that require compiler options. This mirrors Strada's
		// getJSSyntacticDiagnosticsForFile in program.ts.
		if ast.IsSourceFileJS(file) && !ast.IsCheckJSEnabledForFile(file, p.Options()) {
			diags = append(diags, getAdditionalJSSyntacticDiagnostics(file, p.Options())...)
		}
		return diags
	})
}

// getAdditionalJSSyntacticDiagnostics produces option-dependent syntactic diagnostics for JS files
// that aren't covered by the parser or the checker. In Strada, the equivalent logic lives in
// getJSSyntacticDiagnosticsForFile in program.ts. In Corsa, most of that function's checks were
// moved into the parser (checkJSSyntax/checkJSDecoratorSyntax), but checks that depend on compiler
// options can't live in the parser and must remain here. The checker handles these for checked files,
// but doesn't run on unchecked JS files (no checkJs/ts-check).
func getAdditionalJSSyntacticDiagnostics(file *ast.SourceFile, options *core.CompilerOptions) []*ast.Diagnostic {
	if options.ExperimentalDecorators.IsTrue() {
		return nil
	}
	var diags []*ast.Diagnostic
	// Parameter decorators are only valid with experimentalDecorators. Without it,
	// the checker would report this, but the checker doesn't run on unchecked JS files.
	var walk ast.Visitor
	walk = func(node *ast.Node) bool {
		if node.SubtreeFacts()&ast.SubtreeContainsDecorators == 0 {
			return false
		}
		if node.Kind == ast.KindParameter && ast.HasDecorators(node) {
			decorator := core.Find(node.ModifierNodes(), ast.IsDecorator)
			if decorator != nil {
				diags = append(diags, ast.NewDiagnostic(file, decorator.Loc, diagnostics.Decorators_are_not_valid_here))
			}
		}
		node.ForEachChild(walk)
		return false
	}
	file.AsNode().ForEachChild(walk)
	return diags
}

func (p *Program) GetBindDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	if sourceFile != nil {
		binder.BindSourceFile(sourceFile)
	} else {
		p.BindSourceFiles()
	}
	return p.collectDiagnostics(ctx, sourceFile, false /*concurrent*/, func(_ context.Context, file *ast.SourceFile) []*ast.Diagnostic {
		return file.BindDiagnostics()
	})
}

func (p *Program) GetSemanticDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return p.collectCheckerDiagnostics(ctx, sourceFile, p.getSemanticDiagnosticsWithChecker)
}

func (p *Program) GetSemanticDiagnosticsWithoutNoEmitFiltering(ctx context.Context, sourceFiles []*ast.SourceFile) map[*ast.SourceFile][]*ast.Diagnostic {
	allDiags := p.collectCheckerDiagnosticsFromFiles(ctx, sourceFiles, p.getBindAndCheckDiagnosticsWithChecker)
	result := make(map[*ast.SourceFile][]*ast.Diagnostic, len(sourceFiles))
	for i, diags := range allDiags {
		result[sourceFiles[i]] = SortAndDeduplicateDiagnostics(diags)
	}
	return result
}

func (p *Program) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return p.collectCheckerDiagnostics(ctx, sourceFile, p.getSuggestionDiagnosticsWithChecker)
}

func (p *Program) GetProgramDiagnostics() []*ast.Diagnostic {
	return SortAndDeduplicateDiagnostics(core.Concatenate(
		p.programDiagnostics,
		p.includeProcessor.getDiagnostics(p).GetGlobalDiagnostics(),
	))
}

func (p *Program) GetIncludeProcessorDiagnostics(sourceFile *ast.SourceFile) []*ast.Diagnostic {
	if p.SkipTypeChecking(sourceFile, false) {
		return nil
	}
	filtered, _ := p.getDiagnosticsWithPrecedingDirectives(sourceFile, p.includeProcessor.getDiagnostics(p).GetDiagnosticsForFile(sourceFile.FileName()))
	return filtered
}

func (p *Program) SkipTypeChecking(sourceFile *ast.SourceFile, ignoreNoCheck bool) bool {
	return (!ignoreNoCheck && p.Options().NoCheck.IsTrue()) ||
		p.Options().SkipLibCheck.IsTrue() && sourceFile.IsDeclarationFile ||
		p.Options().SkipDefaultLibCheck.IsTrue() && p.IsSourceFileDefaultLibrary(sourceFile.Path()) ||
		p.IsSourceFromProjectReference(sourceFile.Path()) ||
		!p.canIncludeBindAndCheckDiagnostics(sourceFile)
}

func (p *Program) canIncludeBindAndCheckDiagnostics(sourceFile *ast.SourceFile) bool {
	if sourceFile.CheckJsDirective != nil && !sourceFile.CheckJsDirective.Enabled {
		return false
	}

	if sourceFile.ScriptKind == core.ScriptKindTS || sourceFile.ScriptKind == core.ScriptKindTSX || sourceFile.ScriptKind == core.ScriptKindExternal {
		return true
	}

	isJS := sourceFile.ScriptKind == core.ScriptKindJS || sourceFile.ScriptKind == core.ScriptKindJSX
	isCheckJS := isJS && ast.IsCheckJSEnabledForFile(sourceFile, p.Options())
	isPlainJS := ast.IsPlainJSFile(sourceFile, p.Options().CheckJs)

	// By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External
	// - plain JS: .js files with no // ts-check and checkJs: undefined
	// - check JS: .js files with either // ts-check or checkJs: true
	// - external: files that are added by plugins
	return isPlainJS || isCheckJS || sourceFile.ScriptKind == core.ScriptKindDeferred
}

func (p *Program) getSourceFilesToEmit(targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile {
	if targetSourceFile == nil && !forceDtsEmit {
		p.sourceFilesToEmitOnce.Do(func() {
			p.sourceFilesToEmit = getSourceFilesToEmit(p, nil, false)
		})
		return p.sourceFilesToEmit
	}
	return getSourceFilesToEmit(p, targetSourceFile, forceDtsEmit)
}

func (p *Program) verifyCompilerOptions() {
	options := p.Options()

	sourceFile := core.Memoize(func() *ast.SourceFile {
		configFile := p.opts.Config.ConfigFile
		if configFile == nil {
			return nil
		}
		return configFile.SourceFile
	})

	configFilePath := core.Memoize(func() string {
		file := sourceFile()
		if file != nil {
			return file.FileName()
		}
		return ""
	})

	getCompilerOptionsPropertySyntax := core.Memoize(func() *ast.PropertyAssignment {
		return tsoptions.ForEachTsConfigPropArray(sourceFile(), "compilerOptions", core.Identity)
	})

	getCompilerOptionsObjectLiteralSyntax := core.Memoize(func() *ast.ObjectLiteralExpression {
		compilerOptionsProperty := getCompilerOptionsPropertySyntax()
		if compilerOptionsProperty != nil &&
			compilerOptionsProperty.Initializer != nil &&
			ast.IsObjectLiteralExpression(compilerOptionsProperty.Initializer) {
			return compilerOptionsProperty.Initializer.AsObjectLiteralExpression()
		}
		return nil
	})

	createOptionDiagnosticInObjectLiteralSyntax := func(objectLiteral *ast.ObjectLiteralExpression, onKey bool, key1 string, key2 string, message *diagnostics.Message, args ...any) *ast.Diagnostic {
		diag := tsoptions.ForEachPropertyAssignment(objectLiteral, key1, func(property *ast.PropertyAssignment) *ast.Diagnostic {
			return tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), core.IfElse(onKey, property.Name(), property.Initializer), message, args...)
		}, key2)
		if diag != nil {
			p.programDiagnostics = append(p.programDiagnostics, diag)
		}
		return diag
	}

	createCompilerOptionsDiagnostic := func(message *diagnostics.Message, args ...any) *ast.Diagnostic {
		compilerOptionsProperty := getCompilerOptionsPropertySyntax()
		var diag *ast.Diagnostic
		if compilerOptionsProperty != nil {
			diag = tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), compilerOptionsProperty.Name(), message, args...)
		} else {
			diag = ast.NewCompilerDiagnostic(message, args...)
		}
		p.programDiagnostics = append(p.programDiagnostics, diag)
		return diag
	}

	createDiagnosticForOption := func(onKey bool, option1 string, option2 string, message *diagnostics.Message, args ...any) *ast.Diagnostic {
		diag := createOptionDiagnosticInObjectLiteralSyntax(getCompilerOptionsObjectLiteralSyntax(), onKey, option1, option2, message, args...)
		if diag == nil {
			diag = createCompilerOptionsDiagnostic(message, args...)
		}
		return diag
	}

	createDiagnosticForOptionName := func(message *diagnostics.Message, option1 string, option2 string, args ...any) {
		newArgs := make([]any, 0, len(args)+2)
		newArgs = append(newArgs, option1, option2)
		newArgs = append(newArgs, args...)
		createDiagnosticForOption(true /*onKey*/, option1, option2, message, newArgs...)
	}

	createOptionValueDiagnostic := func(option1 string, message *diagnostics.Message, args ...any) {
		createDiagnosticForOption(false /*onKey*/, option1, "", message, args...)
	}

	createRemovedOptionDiagnostic := func(name string, value string, useInstead string) {
		var message *diagnostics.Message
		var args []any
		if value == "" {
			message = diagnostics.Option_0_has_been_removed_Please_remove_it_from_your_configuration
			args = []any{name}
		} else {
			message = diagnostics.Option_0_1_has_been_removed_Please_remove_it_from_your_configuration
			args = []any{name, value}
		}

		diag := createDiagnosticForOption(value == "", name, "", message, args...)
		if useInstead != "" {
			diag.AddMessageChain(ast.NewCompilerDiagnostic(diagnostics.Use_0_instead, useInstead))
		}
	}

	// Removed in TS7

	if options.BaseUrl != "" {
		// BaseUrl will have been turned absolute by this point.
		var useInstead string
		if configFilePath() != "" {
			relative := tspath.GetRelativePathFromFile(configFilePath(), options.BaseUrl, p.comparePathsOptions)
			if !(strings.HasPrefix(relative, "./") || strings.HasPrefix(relative, "../")) {
				relative = "./" + relative
			}
			suggestion := tspath.CombinePaths(relative, "*")
			useInstead = fmt.Sprintf(`"paths": {"*": [%s]}`, core.Must(json.Marshal(suggestion)))
		}
		createRemovedOptionDiagnostic("baseUrl", "", useInstead)
	}

	if options.OutFile != "" {
		createRemovedOptionDiagnostic("outFile", "", "")
	}

	if options.Target == core.ScriptTargetES5 {
		createRemovedOptionDiagnostic("target", "ES5", "")
	}

	if options.Module == core.ModuleKindAMD {
		createRemovedOptionDiagnostic("module", "AMD", "")
	}
	if options.Module == core.ModuleKindSystem {
		createRemovedOptionDiagnostic("module", "System", "")
	}
	if options.Module == core.ModuleKindUMD {
		createRemovedOptionDiagnostic("module", "UMD", "")
	}

	if options.ModuleResolution == core.ModuleResolutionKindClassic {
		createRemovedOptionDiagnostic("moduleResolution", "Classic", "")
	}

	if options.AlwaysStrict.IsFalse() {
		createRemovedOptionDiagnostic("alwaysStrict", "false", "")
	}

	if options.ESModuleInterop.IsFalse() {
		createRemovedOptionDiagnostic("esModuleInterop", "false", "")
	}

	if options.AllowSyntheticDefaultImports.IsFalse() {
		createRemovedOptionDiagnostic("allowSyntheticDefaultImports", "false", "")
	}

	if options.ModuleResolution == core.ModuleResolutionKindNode10 {
		createRemovedOptionDiagnostic("moduleResolution", "node10", "")
	}

	if !options.DownlevelIteration.IsUnknown() {
		createRemovedOptionDiagnostic("downlevelIteration", "", "")
	}

	if options.StrictPropertyInitialization.IsTrue() && !options.GetStrictOptionValue(options.StrictNullChecks) {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks")
	}
	if options.ExactOptionalPropertyTypes.IsTrue() && !options.GetStrictOptionValue(options.StrictNullChecks) {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "exactOptionalPropertyTypes", "strictNullChecks")
	}

	if options.IsolatedDeclarations.IsTrue() {
		if options.GetAllowJS() {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "isolatedDeclarations")
		}
		if !options.GetEmitDeclarations() {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "isolatedDeclarations", "declaration", "composite")
		}
	}

	if options.InlineSourceMap.IsTrue() {
		if options.SourceMap.IsTrue() {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap")
		}
		if options.MapRoot != "" {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap")
		}
	}

	if options.Composite.IsTrue() {
		if options.Declaration.IsFalse() {
			createDiagnosticForOptionName(diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration", "")
		}
		if options.Incremental.IsFalse() {
			createDiagnosticForOptionName(diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration", "")
		}
	}

	if options.TsBuildInfoFile == "" && options.Incremental.IsTrue() && options.ConfigFilePath == "" {
		createCompilerOptionsDiagnostic(diagnostics.Option_incremental_is_only_valid_with_a_known_configuration_file_like_tsconfig_json_or_when_tsBuildInfoFile_is_explicitly_provided)
	}

	p.verifyProjectReferences()

	if options.Composite.IsTrue() {
		var rootPaths collections.Set[tspath.Path]
		for _, fileName := range p.opts.Config.FileNames() {
			rootPaths.Add(p.toPath(fileName))
		}

		for _, file := range p.files {
			if sourceFileMayBeEmitted(file, p, false) && !rootPaths.Has(file.Path()) {
				p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{
					kind: processingDiagnosticKindExplainingFileInclude,
					data: &includeExplainingDiagnostic{
						file:    file.Path(),
						message: diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
						args:    []any{file.FileName(), configFilePath()},
					},
				})
			}
		}
	}

	forEachOptionPathsSyntax := func(callback func(*ast.PropertyAssignment) *ast.Diagnostic) *ast.Diagnostic {
		return tsoptions.ForEachPropertyAssignment(getCompilerOptionsObjectLiteralSyntax(), "paths", callback)
	}

	createDiagnosticForOptionPaths := func(onKey bool, key string, message *diagnostics.Message, args ...any) *ast.Diagnostic {
		diag := forEachOptionPathsSyntax(func(pathProp *ast.PropertyAssignment) *ast.Diagnostic {
			if ast.IsObjectLiteralExpression(pathProp.Initializer) {
				return createOptionDiagnosticInObjectLiteralSyntax(pathProp.Initializer.AsObjectLiteralExpression(), onKey, key, "", message, args...)
			}
			return nil
		})
		if diag == nil {
			diag = createCompilerOptionsDiagnostic(message, args...)
		}
		return diag
	}

	createDiagnosticForOptionPathKeyValue := func(key string, valueIndex int, message *diagnostics.Message, args ...any) *ast.Diagnostic {
		diag := forEachOptionPathsSyntax(func(pathProp *ast.PropertyAssignment) *ast.Diagnostic {
			if ast.IsObjectLiteralExpression(pathProp.Initializer) {
				return tsoptions.ForEachPropertyAssignment(pathProp.Initializer.AsObjectLiteralExpression(), key, func(keyProps *ast.PropertyAssignment) *ast.Diagnostic {
					initializer := keyProps.Initializer
					if ast.IsArrayLiteralExpression(initializer) {
						elements := initializer.ElementList()
						if elements != nil && len(elements.Nodes) > valueIndex {
							diag := tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), elements.Nodes[valueIndex], message, args...)
							p.programDiagnostics = append(p.programDiagnostics, diag)
							return diag
						}
					}
					return nil
				})
			}
			return nil
		})
		if diag == nil {
			diag = createCompilerOptionsDiagnostic(message, args...)
		}
		return diag
	}

	for key, value := range options.Paths.Entries() {
		// !!! This code does not handle cases where where the path mappings have the wrong types,
		// as that information is mostly lost during the parsing process.
		if !hasZeroOrOneAsteriskCharacter(key) {
			createDiagnosticForOptionPaths(true /*onKey*/, key, diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key)
		}
		if value == nil {
			createDiagnosticForOptionPaths(false /*onKey*/, key, diagnostics.Substitutions_for_pattern_0_should_be_an_array, key)
		} else if len(value) == 0 {
			createDiagnosticForOptionPaths(false /*onKey*/, key, diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key)
		}
		for i, subst := range value {
			if !hasZeroOrOneAsteriskCharacter(subst) {
				createDiagnosticForOptionPathKeyValue(key, i, diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key)
			}
			if !tspath.PathIsRelative(subst) && !tspath.PathIsAbsolute(subst) {
				createDiagnosticForOptionPathKeyValue(key, i, diagnostics.Non_relative_paths_are_not_allowed_Did_you_forget_a_leading_Slash)
			}
		}
	}

	if options.SourceMap.IsFalseOrUnknown() && options.InlineSourceMap.IsFalseOrUnknown() {
		if options.InlineSources.IsTrue() {
			createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources", "")
		}
		if options.SourceRoot != "" {
			createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot", "")
		}
	}

	if options.MapRoot != "" && !(options.SourceMap.IsTrue() || options.DeclarationMap.IsTrue()) {
		// Error to specify --mapRoot without --sourcemap
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap")
	}

	if options.DeclarationDir != "" {
		if !options.GetEmitDeclarations() {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite")
		}
	}

	if options.DeclarationMap.IsTrue() && !options.GetEmitDeclarations() {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite")
	}

	if options.Lib != nil && options.NoLib.IsTrue() {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib")
	}

	if options.IsolatedModules.IsTrue() || options.VerbatimModuleSyntax.IsTrue() {
		if options.PreserveConstEnums.IsFalse() {
			createDiagnosticForOptionName(diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_0_is_enabled, core.IfElse(options.VerbatimModuleSyntax.IsTrue(), "verbatimModuleSyntax", "isolatedModules"), "preserveConstEnums")
		}
	}

	if options.OutDir != "" ||
		options.RootDir != "" ||
		options.SourceRoot != "" ||
		options.MapRoot != "" ||
		(options.GetEmitDeclarations() && options.DeclarationDir != "") {
		// !!! sheetal checkSourceFilesBelongToPath - for root Dir and configFile - explaining why file is in the program
		dir := p.CommonSourceDirectory()
		if options.OutDir != "" && dir == "" && core.Some(p.files, func(f *ast.SourceFile) bool { return tspath.GetRootLength(f.FileName()) > 1 }) {
			createDiagnosticForOptionName(diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir", "")
		}
	}

	if !options.NoEmit.IsTrue() &&
		!options.Composite.IsTrue() &&
		options.RootDir == "" &&
		options.ConfigFilePath != "" &&
		(options.OutDir != "" ||
			(options.GetEmitDeclarations() && options.DeclarationDir != "") ||
			options.OutFile != "") {
		// Check if rootDir inferred changed and issue diagnostic
		dir := p.CommonSourceDirectory()
		var emittedFiles []string
		for _, file := range p.files {
			if !file.IsDeclarationFile && sourceFileMayBeEmitted(file, p, false) {
				emittedFiles = append(emittedFiles, file.FileName())
			}
		}
		dir59 := outputpaths.GetComputedCommonSourceDirectory(emittedFiles, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
		if dir59 != "" && tspath.GetCanonicalFileName(dir, p.UseCaseSensitiveFileNames()) != tspath.GetCanonicalFileName(dir59, p.UseCaseSensitiveFileNames()) {
			// change in layout
			var option1 string
			if options.OutFile != "" {
				option1 = "outFile"
			} else if options.OutDir != "" {
				option1 = "outDir"
			} else {
				option1 = "declarationDir"
			}
			var option2 string
			if options.OutFile == "" && options.OutDir != "" {
				option2 = "declarationDir"
			}
			diag := createDiagnosticForOption(
				true, /*onKey*/
				option1,
				option2,
				diagnostics.The_common_source_directory_of_0_is_1_The_rootDir_setting_must_be_explicitly_set_to_this_or_another_path_to_adjust_your_output_s_file_layout,
				tspath.GetBaseFileName(options.ConfigFilePath),
				tspath.GetRelativePathFromFile(options.ConfigFilePath, dir59, p.comparePathsOptions),
			)
			diag.AddMessageChain(ast.NewCompilerDiagnostic(diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashts6_for_migration_information))
		}
	}

	if options.CheckJs.IsTrue() && !options.GetAllowJS() {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs")
	}

	if options.EmitDeclarationOnly.IsTrue() {
		if !options.GetEmitDeclarations() {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite")
		}
	}

	if options.EmitDecoratorMetadata.IsTrue() && options.ExperimentalDecorators.IsFalseOrUnknown() {
		createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators")
	}

	if options.JsxFactory != "" {
		if options.ReactNamespace != "" {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory")
		}
		if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", options.Jsx.String())
		}
		if parser.ParseIsolatedEntityName(options.JsxFactory) == nil {
			createOptionValueDiagnostic("jsxFactory", diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.JsxFactory)
		}
	} else if options.ReactNamespace != "" && !scanner.IsIdentifierText(options.ReactNamespace, core.LanguageVariantStandard) {
		createOptionValueDiagnostic("reactNamespace", diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.ReactNamespace)
	}

	if options.JsxFragmentFactory != "" {
		if options.JsxFactory == "" {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory")
		}
		if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", options.Jsx.String())
		}
		if parser.ParseIsolatedEntityName(options.JsxFragmentFactory) == nil {
			createOptionValueDiagnostic("jsxFragmentFactory", diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.JsxFragmentFactory)
		}
	}

	if options.ReactNamespace != "" {
		if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", options.Jsx.String())
		}
	}

	if options.JsxImportSource != "" {
		if options.Jsx == core.JsxEmitReact {
			createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", options.Jsx.String())
		}
	}

	moduleKind := options.GetEmitModuleKind()

	if options.AllowImportingTsExtensions.IsTrue() && !(options.NoEmit.IsTrue() || options.EmitDeclarationOnly.IsTrue() || options.RewriteRelativeImportExtensions.IsTrue()) {
		createOptionValueDiagnostic("allowImportingTsExtensions", diagnostics.Option_allowImportingTsExtensions_can_only_be_used_when_one_of_noEmit_emitDeclarationOnly_or_rewriteRelativeImportExtensions_is_set)
	}

	moduleResolution := options.GetModuleResolutionKind()
	if options.ResolvePackageJsonExports.IsTrue() && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) {
		createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonExports", "")
	}
	if options.ResolvePackageJsonImports.IsTrue() && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) {
		createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonImports", "")
	}
	if options.CustomConditions != nil && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) {
		createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "customConditions", "")
	}

	if moduleResolution == core.ModuleResolutionKindBundler && !emitModuleKindIsNonNodeESM(moduleKind) && moduleKind != core.ModuleKindPreserve && moduleKind != core.ModuleKindCommonJS {
		createOptionValueDiagnostic("moduleResolution", diagnostics.Option_0_can_only_be_used_when_module_is_set_to_preserve_commonjs_or_es2015_or_later, "bundler")
	}

	if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext &&
		!(core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext) {
		moduleKindName := moduleKind.String()
		var moduleResolutionName string
		if v, ok := core.ModuleKindToModuleResolutionKind[moduleKind]; ok {
			moduleResolutionName = v.String()
		} else {
			moduleResolutionName = "Node16"
		}
		createOptionValueDiagnostic("moduleResolution", diagnostics.Option_moduleResolution_must_be_set_to_0_or_left_unspecified_when_option_module_is_set_to_1, moduleResolutionName, moduleKindName)
	} else if core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext &&
		!(core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext) {
		moduleResolutionName := moduleResolution.String()
		createOptionValueDiagnostic("module", diagnostics.Option_module_must_be_set_to_0_when_option_moduleResolution_is_set_to_1, moduleResolutionName, moduleResolutionName)
	}

	// !!! The below needs filesByName, which is not equivalent to p.filesByPath.

	// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
	if !options.NoEmit.IsTrue() && !options.SuppressOutputPathCheck.IsTrue() {
		var emitFilesSeen collections.Set[string]

		// Verify that all the emit files are unique and don't overwrite input files
		verifyEmitFilePath := func(emitFileName string) {
			if emitFileName != "" {
				emitFilePath := p.toPath(emitFileName)
				// Report error if the output overwrites input file
				if _, ok := p.filesByPath[emitFilePath]; ok {
					diag := ast.NewCompilerDiagnostic(diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName)
					if configFilePath() == "" {
						// The program is from either an inferred project or an external project
						diag.AddMessageChain(ast.NewCompilerDiagnostic(diagnostics.Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig))
					}
					p.blockEmittingOfFile(emitFileName, diag)
				}

				var emitFileKey string
				if !p.Host().FS().UseCaseSensitiveFileNames() {
					emitFileKey = tspath.ToFileNameLowerCase(string(emitFilePath))
				} else {
					emitFileKey = string(emitFilePath)
				}

				// Report error if multiple files write into same file
				if emitFilesSeen.Has(emitFileKey) {
					// Already seen the same emit file - report error
					p.blockEmittingOfFile(emitFileName, ast.NewCompilerDiagnostic(diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName))
				} else {
					emitFilesSeen.Add(emitFileKey)
				}
			}
		}

		outputpaths.ForEachEmittedFile(p, options, func(emitFileNames *outputpaths.OutputPaths, sourceFile *ast.SourceFile) bool {
			verifyEmitFilePath(emitFileNames.JsFilePath())
			verifyEmitFilePath(emitFileNames.SourceMapFilePath())
			verifyEmitFilePath(emitFileNames.DeclarationFilePath())
			verifyEmitFilePath(emitFileNames.DeclarationMapPath())
			return false
		}, p.getSourceFilesToEmit(nil, false), false)
		verifyEmitFilePath(p.opts.Config.GetBuildInfoFileName())
	}
}

func (p *Program) blockEmittingOfFile(emitFileName string, diag *ast.Diagnostic) {
	p.hasEmitBlockingDiagnostics.Add(p.toPath(emitFileName))
	p.programDiagnostics = append(p.programDiagnostics, diag)
}

func (p *Program) IsEmitBlocked(emitFileName string) bool {
	return p.hasEmitBlockingDiagnostics.Has(p.toPath(emitFileName))
}

func (p *Program) verifyProjectReferences() {
	buildInfoFileName := core.IfElse(!p.Options().SuppressOutputPathCheck.IsTrue(), p.opts.Config.GetBuildInfoFileName(), "")
	createDiagnosticForReference := func(config *tsoptions.ParsedCommandLine, index int, message *diagnostics.Message, args ...any) {
		diag := tsoptions.CreateDiagnosticAtReferenceSyntax(config, index, message, args...)
		if diag == nil {
			diag = ast.NewCompilerDiagnostic(message, args...)
		}
		p.programDiagnostics = append(p.programDiagnostics, diag)
	}

	p.RangeResolvedProjectReference(func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool {
		ref := parent.ProjectReferences()[index]
		// !!! Deprecated in 5.0 and removed since 5.5
		// verifyRemovedProjectReference(ref, parent, index);
		if config == nil {
			createDiagnosticForReference(parent, index, diagnostics.File_0_not_found, ref.Path)
			return true
		}
		refOptions := config.CompilerOptions()
		if !refOptions.Composite.IsTrue() || refOptions.NoEmit.IsTrue() {
			if len(parent.FileNames()) > 0 {
				if !refOptions.Composite.IsTrue() {
					createDiagnosticForReference(parent, index, diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.Path)
				}
				if refOptions.NoEmit.IsTrue() {
					createDiagnosticForReference(parent, index, diagnostics.Referenced_project_0_may_not_disable_emit, ref.Path)
				}
			}
		}
		if buildInfoFileName != "" && buildInfoFileName == config.GetBuildInfoFileName() {
			createDiagnosticForReference(parent, index, diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoFileName, ref.Path)
			p.hasEmitBlockingDiagnostics.Add(p.toPath(buildInfoFileName))
		}
		return true
	})
}

func hasZeroOrOneAsteriskCharacter(str string) bool {
	seenAsterisk := false
	for _, ch := range str {
		if ch == '*' {
			if !seenAsterisk {
				seenAsterisk = true
			} else {
				// have already seen asterisk
				return false
			}
		}
	}
	return true
}

func moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution core.ModuleResolutionKind) bool {
	return moduleResolution >= core.ModuleResolutionKindNode16 && moduleResolution <= core.ModuleResolutionKindNodeNext ||
		moduleResolution == core.ModuleResolutionKindBundler
}

func emitModuleKindIsNonNodeESM(moduleKind core.ModuleKind) bool {
	return moduleKind >= core.ModuleKindES2015 && moduleKind <= core.ModuleKindESNext
}

func (p *Program) GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic {
	if len(p.files) == 0 {
		return nil
	}
	if p.compilerCheckerPool != nil {
		return p.compilerCheckerPool.GetGlobalDiagnostics()
	}
	// For external pools (project system), global diagnostics are collected
	// incrementally as checkers are used, not via a bulk query.
	return nil
}

func (p *Program) GetDeclarationDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return p.collectDiagnostics(ctx, sourceFile, true /*concurrent*/, p.getDeclarationDiagnosticsForFile)
}

func FilterNoEmitSemanticDiagnostics(diagnostics []*ast.Diagnostic, options *core.CompilerOptions) []*ast.Diagnostic {
	if !options.NoEmit.IsTrue() {
		return diagnostics
	}
	return core.Filter(diagnostics, func(d *ast.Diagnostic) bool {
		return !d.SkippedOnNoEmit()
	})
}

func (p *Program) getSemanticDiagnosticsWithChecker(ctx context.Context, c *checker.Checker, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return core.Concatenate(
		FilterNoEmitSemanticDiagnostics(p.getBindAndCheckDiagnosticsWithChecker(ctx, c, sourceFile), p.Options()),
		p.GetIncludeProcessorDiagnostics(sourceFile),
	)
}

// getBindAndCheckDiagnosticsWithChecker gets semantic diagnostics for a single file using a
// caller-provided checker, including bind diagnostics, checker diagnostics, and handling
// of @ts-ignore/@ts-expect-error directives.
func (p *Program) getBindAndCheckDiagnosticsWithChecker(ctx context.Context, fileChecker *checker.Checker, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	compilerOptions := p.Options()
	if p.SkipTypeChecking(sourceFile, false) {
		return nil
	}

	// Checker creation forces binding, so bind diagnostics will be populated.
	diags := slices.Clip(sourceFile.BindDiagnostics())
	diags = append(diags, fileChecker.GetDiagnostics(ctx, sourceFile)...)

	isPlainJS := ast.IsPlainJSFile(sourceFile, compilerOptions.CheckJs)
	if isPlainJS {
		return core.Filter(diags, func(d *ast.Diagnostic) bool {
			return plainJSErrors.Has(d.Code())
		})
	}

	isJS := sourceFile.ScriptKind == core.ScriptKindJS || sourceFile.ScriptKind == core.ScriptKindJSX
	isCheckJS := isJS && ast.IsCheckJSEnabledForFile(sourceFile, compilerOptions)
	if isCheckJS {
		diags = append(diags, sourceFile.JSDocDiagnostics()...)
	}

	filtered, directivesByLine := p.getDiagnosticsWithPrecedingDirectives(sourceFile, diags)
	for _, directive := range directivesByLine {
		// Above we changed all used directive kinds to @ts-ignore, so any @ts-expect-error directives that
		// remain are unused and thus errors.
		if directive.Kind == ast.CommentDirectiveKindExpectError {
			filtered = append(filtered, ast.NewDiagnostic(sourceFile, directive.Loc, diagnostics.Unused_ts_expect_error_directive))
		}
	}
	return filtered
}

func (p *Program) getDiagnosticsWithPrecedingDirectives(sourceFile *ast.SourceFile, diags []*ast.Diagnostic) ([]*ast.Diagnostic, map[int]ast.CommentDirective) {
	if len(sourceFile.CommentDirectives) == 0 {
		return diags, nil
	}
	// Build map of directives by line number
	directivesByLine := make(map[int]ast.CommentDirective)
	for _, directive := range sourceFile.CommentDirectives {
		line := scanner.GetECMALineOfPosition(sourceFile, directive.Loc.Pos())
		directivesByLine[line] = directive
	}
	lineStarts := scanner.GetECMALineStarts(sourceFile)
	filtered := make([]*ast.Diagnostic, 0, len(diags))
	for _, diagnostic := range diags {
		ignoreDiagnostic := false
		for line := scanner.ComputeLineOfPosition(lineStarts, diagnostic.Pos()) - 1; line >= 0; line-- {
			// If line contains a @ts-ignore or @ts-expect-error directive, ignore this diagnostic and change
			// the directive kind to @ts-ignore to indicate it was used.
			if directive, ok := directivesByLine[line]; ok {
				ignoreDiagnostic = true
				directive.Kind = ast.CommentDirectiveKindIgnore
				directivesByLine[line] = directive
				break
			}
			// Stop searching backwards when we encounter a line that isn't blank or a comment.
			if !isCommentOrBlankLine(sourceFile.Text(), int(lineStarts[line])) {
				break
			}
		}
		if !ignoreDiagnostic {
			filtered = append(filtered, diagnostic)
		}
	}
	return filtered, directivesByLine
}

func (p *Program) getDeclarationDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	if sourceFile.IsDeclarationFile {
		return []*ast.Diagnostic{}
	}

	if cached, ok := p.declarationDiagnosticCache.Load(sourceFile); ok {
		return cached
	}

	host, done := newEmitHost(ctx, p, sourceFile)
	defer done()
	diagnostics := getDeclarationDiagnostics(host, sourceFile)
	diagnostics, _ = p.declarationDiagnosticCache.LoadOrStore(sourceFile, diagnostics)
	return diagnostics
}

func (p *Program) getSuggestionDiagnosticsWithChecker(ctx context.Context, fileChecker *checker.Checker, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	if p.SkipTypeChecking(sourceFile, false) {
		return nil
	}

	// Checker creation forces binding, so bind suggestion diagnostics will be populated.
	diags := slices.Clip(sourceFile.BindSuggestionDiagnostics)
	diags = append(diags, fileChecker.GetSuggestionDiagnostics(ctx, sourceFile)...)

	return diags
}

func isCommentOrBlankLine(text string, pos int) bool {
	for pos < len(text) && (text[pos] == ' ' || text[pos] == '\t') {
		pos++
	}
	return pos == len(text) ||
		pos < len(text) && (text[pos] == '\r' || text[pos] == '\n') ||
		pos+1 < len(text) && text[pos] == '/' && text[pos+1] == '/'
}

func SortAndDeduplicateDiagnostics(diagnostics []*ast.Diagnostic) []*ast.Diagnostic {
	diagnostics = slices.Clone(diagnostics)
	slices.SortFunc(diagnostics, ast.CompareDiagnostics)
	return compactAndMergeRelatedInfos(diagnostics)
}

// Remove duplicate diagnostics and, for sequences of diagnostics that differ only by related information,
// create a single diagnostic with sorted and deduplicated related information.
func compactAndMergeRelatedInfos(diagnostics []*ast.Diagnostic) []*ast.Diagnostic {
	if len(diagnostics) < 2 {
		return diagnostics
	}
	i := 0
	j := 0
	for i < len(diagnostics) {
		d := diagnostics[i]
		n := 1
		for i+n < len(diagnostics) && ast.EqualDiagnosticsNoRelatedInfo(d, diagnostics[i+n]) {
			n++
		}
		if n > 1 {
			var relatedInfos []*ast.Diagnostic
			for k := range n {
				relatedInfos = append(relatedInfos, diagnostics[i+k].RelatedInformation()...)
			}
			if relatedInfos != nil {
				slices.SortFunc(relatedInfos, ast.CompareDiagnostics)
				relatedInfos = slices.CompactFunc(relatedInfos, ast.EqualDiagnostics)
				d = d.Clone().SetRelatedInfo(relatedInfos)
			}
		}
		diagnostics[j] = d
		i += n
		j++
	}
	clear(diagnostics[j:])
	return diagnostics[:j]
}

func (p *Program) LineCount() int {
	var count int
	for _, file := range p.files {
		count += len(file.ECMALineMap())
	}
	return count
}

func (p *Program) IdentifierCount() int {
	var count int
	for _, file := range p.files {
		count += file.IdentifierCount
	}
	return count
}

func (p *Program) SymbolCount() int {
	var count int
	for _, file := range p.files {
		count += file.SymbolCount
	}
	var val atomic.Uint32
	val.Store(uint32(count))
	p.ForEachCheckerParallel(func(_ int, c *checker.Checker) {
		val.Add(c.SymbolCount)
	})
	return int(val.Load())
}

func (p *Program) TypeCount() int {
	var val atomic.Uint32
	p.ForEachCheckerParallel(func(_ int, c *checker.Checker) {
		val.Add(c.TypeCount)
	})
	return int(val.Load())
}

func (p *Program) InstantiationCount() int {
	var val atomic.Uint32
	p.ForEachCheckerParallel(func(_ int, c *checker.Checker) {
		val.Add(c.TotalInstantiationCount)
	})
	return int(val.Load())
}

func (p *Program) Program() *Program {
	return p
}

func (p *Program) GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData {
	return p.sourceFileMetaDatas[path]
}

func (p *Program) GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind {
	return ast.GetEmitModuleFormatOfFileWorker(sourceFile.FileName(), p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile), p.GetSourceFileMetaData(sourceFile.Path()))
}

func (p *Program) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, location *ast.StringLiteralLike) core.ResolutionMode {
	return getEmitSyntaxForUsageLocationWorker(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], location, p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile))
}

func (p *Program) GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ResolutionMode {
	return ast.GetImpliedNodeFormatForEmitWorker(sourceFile.FileName(), p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile).GetEmitModuleKind(), p.GetSourceFileMetaData(sourceFile.Path()))
}

func (p *Program) GetModeForUsageLocation(sourceFile ast.HasFileName, location *ast.StringLiteralLike) core.ResolutionMode {
	return getModeForUsageLocation(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], location, p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile))
}

func (p *Program) GetDefaultResolutionModeForFile(sourceFile ast.HasFileName) core.ResolutionMode {
	return getDefaultResolutionModeForFile(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile))
}

func (p *Program) IsSourceFileDefaultLibrary(path tspath.Path) bool {
	_, ok := p.libFiles[path]
	return ok
}

func (p *Program) IsGlobalTypingsFile(fileName string) bool {
	if !tspath.IsDeclarationFileName(fileName) {
		return false
	}
	return tspath.ContainsPath(p.GetGlobalTypingsCacheLocation(), fileName, p.comparePathsOptions)
}

func (p *Program) GetDefaultLibFile(path tspath.Path) *LibFile {
	if libFile, ok := p.libFiles[path]; ok {
		return libFile
	}
	return nil
}

func (p *Program) CommonSourceDirectory() string {
	p.commonSourceDirectoryOnce.Do(func() {
		files := func() []string {
			return core.MapFiltered(p.files, func(file *ast.SourceFile) (string, bool) {
				return file.FileName(), sourceFileMayBeEmitted(file, p, false /*forceDtsEmit*/) && !file.IsDeclarationFile
			})
		}
		p.commonSourceDirectory = outputpaths.GetCommonSourceDirectory(
			p.Options(),
			files,
			p.GetCurrentDirectory(),
			p.UseCaseSensitiveFileNames(),
			p.checkSourceFilesBelongToPath,
		)
	})
	return p.commonSourceDirectory
}

func (p *Program) checkSourceFilesBelongToPath(sourceFiles []string, rootDirectory string) bool {
	allFilesBelongToPath := true
	for _, file := range sourceFiles {
		absoluteSourceFilePath := tspath.GetCanonicalFileName(tspath.GetNormalizedAbsolutePath(file, p.GetCurrentDirectory()), p.UseCaseSensitiveFileNames())
		if !tspath.ContainsPath(rootDirectory, file, p.comparePathsOptions) {
			p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{
				kind: processingDiagnosticKindExplainingFileInclude,
				data: &includeExplainingDiagnostic{
					file:    tspath.Path(absoluteSourceFilePath),
					message: diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
					args:    []any{file, rootDirectory},
				},
			})
			allFilesBelongToPath = false
		}
	}

	return allFilesBelongToPath
}

type WriteFileData struct {
	SourceMapUrlPos int
	BuildInfo       any
	Diagnostics     []*ast.Diagnostic
	SkippedDtsWrite bool
}

type WriteFile func(fileName string, text string, data *WriteFileData) error

type EmitOptions struct {
	TargetSourceFile *ast.SourceFile // Single file to emit. If `nil`, emits all files
	EmitOnly         EmitOnly
	WriteFile        WriteFile
}

type EmitResult struct {
	EmitSkipped  bool
	Diagnostics  []*ast.Diagnostic      // Contains declaration emit diagnostics
	EmittedFiles []string               // Array of files the compiler wrote to disk
	SourceMaps   []*SourceMapEmitResult // Array of sourceMapData if compiler emitted sourcemaps
}

type SourceMapEmitResult struct {
	InputSourceFileNames []string // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMap.sources list
	SourceMap            *sourcemap.RawSourceMap
	GeneratedFile        string
}

func (p *Program) Emit(ctx context.Context, options EmitOptions) *EmitResult {
	if tr := p.opts.Tracing; tr != nil {
		defer tr.Push(tracing.PhaseEmit, "emit", nil, true)()
	}

	if options.EmitOnly != EmitOnlyForcedDts {
		result := HandleNoEmitOnError(
			ctx,
			p,
			options.TargetSourceFile,
		)
		if result != nil || ctx.Err() != nil {
			return result
		}
	}

	newLine := p.Options().NewLine.GetNewLineCharacter()
	writerPool := &sync.Pool{
		New: func() any {
			return printer.NewTextWriter(newLine, 0)
		},
	}
	wg := core.NewWorkGroup(p.SingleThreaded())
	var emitters []*emitter
	sourceFiles := p.getSourceFilesToEmit(options.TargetSourceFile, options.EmitOnly == EmitOnlyForcedDts)

	for _, sourceFile := range sourceFiles {
		emitter := &emitter{
			writer:     nil,
			sourceFile: sourceFile,
			emitOnly:   options.EmitOnly,
			writeFile:  options.WriteFile,
			tr:         p.opts.Tracing,
		}
		emitters = append(emitters, emitter)
		wg.Queue(func() {
			host, done := newEmitHost(ctx, p, sourceFile)
			defer done()
			emitter.host = host

			// take an unused writer
			writer := writerPool.Get().(printer.EmitTextWriter)
			writer.Clear()

			// attach writer and perform emit
			emitter.writer = writer
			emitter.paths = outputpaths.GetOutputPathsFor(sourceFile, host.Options(), host, options.EmitOnly == EmitOnlyForcedDts)
			emitter.emit()
			emitter.writer = nil

			// put the writer back in the pool
			writerPool.Put(writer)
		})
	}

	// wait for emit to complete
	wg.RunAndWait()

	// collect results from emit, preserving input order
	return CombineEmitResults(core.Map(emitters, func(e *emitter) *EmitResult {
		return &e.emitResult
	}))
}

func CombineEmitResults(results []*EmitResult) *EmitResult {
	result := &EmitResult{}
	for _, emitResult := range results {
		if emitResult == nil {
			continue // Skip nil results
		}
		if emitResult.EmitSkipped {
			result.EmitSkipped = true
		}
		result.Diagnostics = append(result.Diagnostics, emitResult.Diagnostics...)
		result.EmittedFiles = append(result.EmittedFiles, emitResult.EmittedFiles...)
		if emitResult.SourceMaps != nil {
			result.SourceMaps = append(result.SourceMaps, emitResult.SourceMaps...)
		}
	}
	return result
}

type ProgramLike interface {
	Options() *core.CompilerOptions
	GetSourceFile(path string) *ast.SourceFile
	GetSourceFiles() []*ast.SourceFile
	GetConfigFileParsingDiagnostics() []*ast.Diagnostic
	GetSyntacticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic
	GetBindDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic
	GetProgramDiagnostics() []*ast.Diagnostic
	GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic
	GetSemanticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic
	GetDeclarationDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic
	GetSuggestionDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic
	Emit(ctx context.Context, options EmitOptions) *EmitResult
	CommonSourceDirectory() string
	IsSourceFileDefaultLibrary(path tspath.Path) bool
	Program() *Program
}

func HandleNoEmitOnError(ctx context.Context, program ProgramLike, file *ast.SourceFile) *EmitResult {
	if !program.Options().NoEmitOnError.IsTrue() {
		return nil // No emit on error is not set, so we can proceed with emitting
	}

	diagnostics := GetDiagnosticsOfAnyProgram(
		ctx,
		program,
		file,
		true,
		program.GetBindDiagnostics,
		program.GetSemanticDiagnostics,
	)
	if len(diagnostics) == 0 {
		return nil // No diagnostics, so we can proceed with emitting
	}
	return &EmitResult{
		Diagnostics: diagnostics,
		EmitSkipped: true,
	}
}

func GetDiagnosticsOfAnyProgram(
	ctx context.Context,
	program ProgramLike,
	file *ast.SourceFile,
	skipNoEmitCheckForDtsDiagnostics bool,
	getBindDiagnostics func(context.Context, *ast.SourceFile) []*ast.Diagnostic,
	getSemanticDiagnostics func(context.Context, *ast.SourceFile) []*ast.Diagnostic,
) []*ast.Diagnostic {
	allDiagnostics := slices.Clip(program.GetConfigFileParsingDiagnostics())
	configFileParsingDiagnosticsLength := len(allDiagnostics)

	allDiagnostics = append(allDiagnostics, program.GetSyntacticDiagnostics(ctx, file)...)

	// If we didn't have any syntactic errors, then also try getting the program (options),
	// global and semantic errors.
	if len(allDiagnostics) == configFileParsingDiagnosticsLength {
		allDiagnostics = append(allDiagnostics, program.GetProgramDiagnostics()...)

		// Do binding early so we can track the time.
		getBindDiagnostics(ctx, file)

		if program.Options().ListFilesOnly.IsFalseOrUnknown() {
			allDiagnostics = append(allDiagnostics, program.GetGlobalDiagnostics(ctx)...)

			if len(allDiagnostics) == configFileParsingDiagnosticsLength {
				allDiagnostics = append(allDiagnostics, getSemanticDiagnostics(ctx, file)...)
				// Ask for the global diagnostics again (they were empty above); we may have found new during checking, e.g. missing globals.
				allDiagnostics = append(allDiagnostics, program.GetGlobalDiagnostics(ctx)...)
			}

			if (skipNoEmitCheckForDtsDiagnostics || program.Options().NoEmit.IsTrue()) && program.Options().GetEmitDeclarations() && len(allDiagnostics) == configFileParsingDiagnosticsLength {
				allDiagnostics = append(allDiagnostics, program.GetDeclarationDiagnostics(ctx, file)...)
			}
		}
	}
	return allDiagnostics
}

func (p *Program) toPath(filename string) tspath.Path {
	return tspath.ToPath(filename, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())
}

func (p *Program) GetSourceFile(filename string) *ast.SourceFile {
	path := p.toPath(filename)
	return p.GetSourceFileByPath(path)
}

func (p *Program) GetSourceFileForResolvedModule(fileName string) *ast.SourceFile {
	file := p.GetSourceFile(fileName)
	if file == nil {
		filename := p.GetParseFileRedirect(fileName)
		if filename != "" {
			return p.GetSourceFile(filename)
		}
	}
	return file
}

func (p *Program) FilesByPath() map[tspath.Path]*ast.SourceFile {
	return p.filesByPath
}

func (p *Program) GetSourceFileByPath(path tspath.Path) *ast.SourceFile {
	return p.filesByPath[path]
}

func (p *Program) HasSameFileNames(other *Program) bool {
	return maps.EqualFunc(p.filesByPath, other.filesByPath, func(a, b *ast.SourceFile) bool {
		// checks for casing differences on case-insensitive file systems
		return a.FileName() == b.FileName()
	}) && maps.EqualFunc(p.redirectFilesByPath, other.redirectFilesByPath, func(a, b *redirectsFile) bool {
		return a.FileName() == b.FileName()
	})
}

func (p *Program) GetSourceFiles() []*ast.SourceFile {
	return p.files
}

// Testing only
func (p *Program) GetIncludeReasons() map[tspath.Path][]*FileIncludeReason {
	return p.includeProcessor.fileIncludeReasons
}

// Testing only
func (p *Program) IsMissingPath(path tspath.Path) bool {
	return slices.ContainsFunc(p.missingFiles, func(missingPath string) bool {
		return p.toPath(missingPath) == path
	})
}

func (p *Program) ExplainFiles(w io.Writer, locale locale.Locale) {
	toRelativeFileName := func(fileName string) string {
		return tspath.GetRelativePathFromDirectory(p.GetCurrentDirectory(), fileName, p.comparePathsOptions)
	}
	filesExplained := 0
	explainFile := func(file ast.HasFileName) {
		fmt.Fprintln(w, toRelativeFileName(file.FileName()))
		for _, reason := range p.includeProcessor.fileIncludeReasons[file.Path()] {
			fmt.Fprintln(w, "  ", reason.toDiagnostic(p, true).Localize(locale))
		}
		for _, diag := range p.includeProcessor.explainRedirectAndImpliedFormat(p, file.Path(), toRelativeFileName) {
			fmt.Fprintln(w, "  ", diag.Localize(locale))
		}
		filesExplained++
	}

	redirectFiles := slices.Collect(maps.Values(p.redirectFilesByPath))
	slices.SortFunc(redirectFiles, func(a, b *redirectsFile) int {
		return a.index - b.index
	})

	files := p.GetSourceFiles()
	sourceFileIndex := 0
	explainSourceFiles := func(endIndex int) {
		for filesExplained < endIndex {
			explainFile(files[sourceFileIndex])
			sourceFileIndex++
		}
	}

	for _, redirectFile := range redirectFiles {
		// Explain all sourceFiles till we reach this redirectFile index
		explainSourceFiles(redirectFile.index)
		explainFile(redirectFile)
	}

	// Explain any remaining sourceFiles
	explainSourceFiles(len(files) + len(redirectFiles))
}

func (p *Program) GetLibFileFromReference(ref *ast.FileReference) *ast.SourceFile {
	path, ok := tsoptions.GetLibFileName(ref.FileName)
	if !ok {
		return nil
	}
	if sourceFile, ok := p.filesByPath[tspath.Path(path)]; ok {
		return sourceFile
	}
	return nil
}

func (p *Program) GetResolvedTypeReferenceDirectiveFromTypeReferenceDirective(typeRef *ast.FileReference, sourceFile *ast.SourceFile) *module.ResolvedTypeReferenceDirective {
	if resolutions, ok := p.typeResolutionsInFile[sourceFile.Path()]; ok {
		if resolved, ok := resolutions[module.ModeAwareCacheKey{Name: typeRef.FileName, Mode: p.getModeForTypeReferenceDirectiveInFile(typeRef, sourceFile)}]; ok {
			return resolved
		}
	}
	return nil
}

func (p *Program) GetResolvedTypeReferenceDirectives() map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective] {
	return p.typeResolutionsInFile
}

func (p *Program) getModeForTypeReferenceDirectiveInFile(ref *ast.FileReference, sourceFile *ast.SourceFile) core.ResolutionMode {
	if ref.ResolutionMode != core.ResolutionModeNone {
		return ref.ResolutionMode
	}
	return p.GetDefaultResolutionModeForFile(sourceFile)
}

func (p *Program) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool {
	return p.sourceFilesFoundSearchingNodeModules.Has(file.Path())
}

func (p *Program) GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node) {
	if result := p.jsxRuntimeImportSpecifiers[path]; result != nil {
		return result.moduleReference, result.specifier
	}
	return "", nil
}

func (p *Program) GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node {
	return p.importHelpersImportSpecifiers[path]
}

func (p *Program) SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool {
	return sourceFileMayBeEmitted(sourceFile, p, forceDtsEmit)
}

func (p *Program) ResolvedPackageNames() *collections.Set[string] {
	return p.collectPackageNames().resolved
}

func (p *Program) UnresolvedPackageNames() *collections.Set[string] {
	return p.collectPackageNames().unresolved
}

func (p *Program) DeepImportPackageNames() *collections.Set[string] {
	return p.collectPackageNames().deepImportPackages
}

func (p *Program) collectPackageNames() *packageNamesInfo {
	return p.packageNames.getValue(func() *packageNamesInfo {
		packageNames := &packageNamesInfo{&collections.Set[string]{}, &collections.Set[string]{}, &collections.Set[string]{}}
		for _, file := range p.files {
			if p.IsSourceFileDefaultLibrary(file.Path()) || p.IsSourceFileFromExternalLibrary(file) || strings.Contains(file.FileName(), "/node_modules/") {
				// Checking for /node_modules/ is a little imprecise, but ATA treats locally installed typings
				// as root files, which would not pass IsSourceFileFromExternalLibrary.
				continue
			}
			for _, imp := range file.Imports() {
				if tspath.IsExternalModuleNameRelative(imp.Text()) {
					continue
				}
				if resolvedModules, ok := p.resolvedModules[file.Path()]; ok {
					key := module.ModeAwareCacheKey{Name: imp.Text(), Mode: p.GetModeForUsageLocation(file, imp)}
					if resolvedModule, ok := resolvedModules[key]; ok && resolvedModule.IsResolved() {
						if !resolvedModule.IsExternalLibraryImport {
							continue
						}
						// Priority order for getting package name:
						// 1. PackageId.Name (requires both name and version in package.json)
						name := resolvedModule.PackageId.Name
						if name == "" {
							// 2. GetPackageScopeForPath - get name from package.json in the package directory
							if packageScope := p.resolver.GetPackageScopeForPath(resolvedModule.ResolvedFileName); packageScope != nil && packageScope.Exists() {
								if scopeName, ok := packageScope.Contents.Name.GetValue(); ok {
									name = scopeName
								}
							}
						}
						if name == "" {
							// 3. GetPackageNameFromDirectory - extract from node_modules path
							name = modulespecifiers.GetPackageNameFromDirectory(resolvedModule.ResolvedFileName)
						}
						// 4. If all fail, don't add empty string
						if name != "" {
							packageNames.resolved.Add(name)
							// Detect deep imports: subpath imports in packages without exports.
							// These are imports like "lodash/fp" where the package has no exports
							// map, so auto-import can only find them via recursive directory search.
							_, rest := module.ParsePackageName(imp.Text())
							if rest != "" {
								if scope := p.resolver.GetPackageScopeForPath(resolvedModule.ResolvedFileName); scope != nil && scope.Exists() && !scope.Contents.Exports.IsPresent() {
									packageNames.deepImportPackages.Add(module.GetPackageNameFromTypesPackageName(name))
								}
							}
						}
						continue
					}
				}
				packageNames.unresolved.Add(imp.Text())
			}
		}
		return packageNames
	})
}

func (p *Program) IsLibFile(sourceFile *ast.SourceFile) bool {
	_, ok := p.libFiles[sourceFile.Path()]
	return ok
}

func (p *Program) HasTSFile() bool {
	p.hasTSFileOnce.Do(func() {
		for _, file := range p.files {
			if tspath.HasImplementationTSFileExtension(file.FileName()) {
				p.hasTSFile = true
				break
			}
		}
	})
	return p.hasTSFile
}

func (p *Program) GetSymlinkCache() *symlinks.KnownSymlinks {
	return p.knownSymlinks.getValue(func() *symlinks.KnownSymlinks {
		knownSymlinks := symlinks.NewKnownSymlink(p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames())

		// Resolved modules store realpath information when they're resolved inside node_modules
		if len(p.resolvedModules) > 0 || len(p.typeResolutionsInFile) > 0 {
			knownSymlinks.SetSymlinksFromResolutions(p.ForEachResolvedModule, p.ForEachResolvedTypeReferenceDirective)
		}

		// Check other dependencies for symlinks
		var seenPackageJsons collections.Set[tspath.Path]
		for filePath, meta := range p.sourceFileMetaDatas {
			if meta.PackageJsonDirectory == "" ||
				!p.SourceFileMayBeEmitted(p.GetSourceFileByPath(filePath), false) ||
				!seenPackageJsons.AddIfAbsent(p.toPath(meta.PackageJsonDirectory)) {
				continue
			}
			packageJsonName := tspath.CombinePaths(meta.PackageJsonDirectory, "package.json")
			info := p.GetPackageJsonInfo(packageJsonName)
			if info.GetContents() == nil {
				continue
			}

			for dep := range info.GetContents().GetRuntimeDependencyNames().Keys() {
				// Skip work in common case: we already saved a symlink for this package directory
				// in the node_modules adjacent to this package.json
				possibleDirectoryPath := p.toPath(tspath.CombinePaths(meta.PackageJsonDirectory, "node_modules", dep))
				if knownSymlinks.HasDirectory(possibleDirectoryPath) {
					continue
				}
				if !strings.HasPrefix(dep, "@types") {
					possibleTypesDirectoryPath := p.toPath(tspath.CombinePaths(meta.PackageJsonDirectory, "node_modules", module.GetTypesPackageName(dep)))
					if knownSymlinks.HasDirectory(possibleTypesDirectoryPath) {
						continue
					}
				}

				if packageResolution := p.resolver.ResolvePackageDirectory(dep, packageJsonName, core.ResolutionModeCommonJS, nil); packageResolution.IsResolved() {
					knownSymlinks.ProcessResolution(
						tspath.CombinePaths(packageResolution.OriginalPath, "package.json"),
						tspath.CombinePaths(packageResolution.ResolvedFileName, "package.json"),
					)
				}
			}
		}
		return knownSymlinks
	})
}

func (p *Program) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode) *module.ResolvedModule {
	resolved, _ := p.resolver.ResolveModuleName(moduleName, containingFile, resolutionMode, nil)
	return resolved
}

func (p *Program) ForEachResolvedModule(callback func(resolution *module.ResolvedModule, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile) {
	forEachResolution(p.resolvedModules, callback, file)
}

func (p *Program) ForEachResolvedTypeReferenceDirective(callback func(resolution *module.ResolvedTypeReferenceDirective, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile) {
	forEachResolution(p.typeResolutionsInFile, callback, file)
}

func forEachResolution[T any](resolutionCache map[tspath.Path]module.ModeAwareCache[T], callback func(resolution T, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile) {
	if file != nil {
		if resolutions, ok := resolutionCache[file.Path()]; ok {
			for key, resolution := range resolutions {
				callback(resolution, key.Name, key.Mode, file.Path())
			}
		}
	} else {
		for filePath, resolutions := range resolutionCache {
			for key, resolution := range resolutions {
				callback(resolution, key.Name, key.Mode, filePath)
			}
		}
	}
}

var plainJSErrors = collections.NewSetFromItems(
	// binder errors
	diagnostics.Cannot_redeclare_block_scoped_variable_0.Code(),
	diagnostics.A_module_cannot_have_multiple_default_exports.Code(),
	diagnostics.Another_export_default_is_here.Code(),
	diagnostics.The_first_export_default_is_here.Code(),
	diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module.Code(),
	diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode.Code(),
	diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here.Code(),
	diagnostics.X_constructor_is_a_reserved_word.Code(),
	diagnostics.X_delete_cannot_be_called_on_an_identifier_in_strict_mode.Code(),
	diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode.Code(),
	diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.Code(),
	diagnostics.Invalid_use_of_0_in_strict_mode.Code(),
	diagnostics.A_label_is_not_allowed_here.Code(),
	diagnostics.X_with_statements_are_not_allowed_in_strict_mode.Code(),
	// grammar errors
	diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement.Code(),
	diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement.Code(),
	diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name.Code(),
	diagnostics.A_class_member_cannot_have_the_0_keyword.Code(),
	diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name.Code(),
	diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement.Code(),
	diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement.Code(),
	diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement.Code(),
	diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration.Code(),
	diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context.Code(),
	diagnostics.A_destructuring_declaration_must_have_an_initializer.Code(),
	diagnostics.A_get_accessor_cannot_have_parameters.Code(),
	diagnostics.A_rest_element_cannot_contain_a_binding_pattern.Code(),
	diagnostics.A_rest_element_cannot_have_a_property_name.Code(),
	diagnostics.A_rest_element_cannot_have_an_initializer.Code(),
	diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern.Code(),
	diagnostics.A_rest_parameter_cannot_have_an_initializer.Code(),
	diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list.Code(),
	diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma.Code(),
	diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block.Code(),
	diagnostics.A_set_accessor_cannot_have_rest_parameter.Code(),
	diagnostics.A_set_accessor_must_have_exactly_one_parameter.Code(),
	diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module.Code(),
	diagnostics.An_export_declaration_cannot_have_modifiers.Code(),
	diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module.Code(),
	diagnostics.An_import_declaration_cannot_have_modifiers.Code(),
	diagnostics.An_object_member_cannot_be_declared_optional.Code(),
	diagnostics.Argument_of_dynamic_import_cannot_be_spread_element.Code(),
	diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable.Code(),
	diagnostics.Cannot_redeclare_identifier_0_in_catch_clause.Code(),
	diagnostics.Catch_clause_variable_cannot_have_an_initializer.Code(),
	diagnostics.Class_decorators_can_t_be_used_with_static_private_identifier_Consider_removing_the_experimental_decorator.Code(),
	diagnostics.Classes_can_only_extend_a_single_class.Code(),
	diagnostics.Classes_may_not_have_a_field_named_constructor.Code(),
	diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern.Code(),
	diagnostics.Duplicate_label_0.Code(),
	diagnostics.Dynamic_imports_can_only_accept_a_module_specifier_and_an_optional_set_of_attributes_as_arguments.Code(),
	diagnostics.X_for_await_loops_cannot_be_used_inside_a_class_static_block.Code(),
	diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression.Code(),
	diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name.Code(),
	diagnostics.JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array.Code(),
	diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names.Code(),
	diagnostics.Jump_target_cannot_cross_function_boundary.Code(),
	diagnostics.Line_terminator_not_permitted_before_arrow.Code(),
	diagnostics.Modifiers_cannot_appear_here.Code(),
	diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement.Code(),
	diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement.Code(),
	diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.Code(),
	diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.Code(),
	diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier.Code(),
	diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain.Code(),
	diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_async.Code(),
	diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer.Code(),
	diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer.Code(),
	diagnostics.Trailing_comma_not_allowed.Code(),
	diagnostics.Variable_declaration_list_cannot_be_empty.Code(),
	diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses.Code(),
	diagnostics.X_0_expected.Code(),
	diagnostics.X_0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2.Code(),
	diagnostics.X_0_list_cannot_be_empty.Code(),
	diagnostics.X_0_modifier_already_seen.Code(),
	diagnostics.X_0_modifier_cannot_appear_on_a_constructor_declaration.Code(),
	diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element.Code(),
	diagnostics.X_0_modifier_cannot_appear_on_a_parameter.Code(),
	diagnostics.X_0_modifier_cannot_appear_on_class_elements_of_this_kind.Code(),
	diagnostics.X_0_modifier_cannot_be_used_here.Code(),
	diagnostics.X_0_modifier_must_precede_1_modifier.Code(),
	diagnostics.X_0_declarations_can_only_be_declared_inside_a_block.Code(),
	diagnostics.X_0_declarations_must_be_initialized.Code(),
	diagnostics.X_extends_clause_already_seen.Code(),
	diagnostics.X_let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations.Code(),
	diagnostics.Class_constructor_may_not_be_a_generator.Code(),
	diagnostics.Class_constructor_may_not_be_an_accessor.Code(),
	diagnostics.X_await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.Code(),
	diagnostics.X_await_using_statements_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.Code(),
	diagnostics.Private_field_0_must_be_declared_in_an_enclosing_class.Code(),
	// Type errors
	diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value.Code(),
)
