From 32cf6b4677830d87696a44c8947fe43cf839b964 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 2 Sep 2025 09:42:17 -0700 Subject: [PATCH 01/28] Make resolver swappable --- internal/compiler/fileloader.go | 6 +++--- internal/compiler/host.go | 6 ++++++ internal/execute/build/compilerHost.go | 6 ++++++ internal/execute/build/host.go | 6 ++++++ internal/module/resolver.go | 7 +++++++ internal/project/compilerhost.go | 6 ++++++ 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index 9fbbc49057..0ba807719b 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -29,7 +29,7 @@ type LibFile struct { type fileLoader struct { opts ProgramOptions - resolver *module.Resolver + resolver module.ResolverInterface defaultLibraryPath string comparePathsOptions tspath.ComparePathsOptions supportedExtensions []string @@ -52,7 +52,7 @@ type fileLoader struct { } type processedFiles struct { - resolver *module.Resolver + resolver module.ResolverInterface files []*ast.SourceFile filesByPath map[tspath.Path]*ast.SourceFile projectReferenceFileMapper *projectReferenceFileMapper @@ -104,7 +104,7 @@ func processAllProgramFiles( includeProcessor: &includeProcessor{}, } loader.addProjectReferenceTasks(singleThreaded) - loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName) + loader.resolver = loader.opts.Host.MakeResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName) for index, rootFile := range rootFiles { loader.addRootTask(rootFile, nil, &fileIncludeReason{kind: fileIncludeKindRootFile, data: index}) } diff --git a/internal/compiler/host.go b/internal/compiler/host.go index 68f3cf620a..479301c3e1 100644 --- a/internal/compiler/host.go +++ b/internal/compiler/host.go @@ -3,6 +3,7 @@ package compiler import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/parser" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" @@ -17,6 +18,7 @@ type CompilerHost interface { Trace(msg string) GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine + MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface } var _ CompilerHost = (*compilerHost)(nil) @@ -86,3 +88,7 @@ func (h *compilerHost) GetResolvedProjectReference(fileName string, path tspath. commandLine, _ := tsoptions.GetParsedCommandLineOfConfigFilePath(fileName, path, nil, h, h.extendedConfigCache) return commandLine } + +func (h *compilerHost) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { + return module.NewResolver(host, options, typingsLocation, projectName) +} diff --git a/internal/execute/build/compilerHost.go b/internal/execute/build/compilerHost.go index f11f06b9fc..e59db25716 100644 --- a/internal/execute/build/compilerHost.go +++ b/internal/execute/build/compilerHost.go @@ -3,6 +3,8 @@ package build import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" @@ -35,6 +37,10 @@ func (h *compilerHost) GetSourceFile(opts ast.SourceFileParseOptions) *ast.Sourc return h.host.GetSourceFile(opts) } +func (h *compilerHost) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { + return h.host.MakeResolver(host, options, typingsLocation, projectName) +} + func (h *compilerHost) GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine { return h.host.GetResolvedProjectReference(fileName, path) } diff --git a/internal/execute/build/host.go b/internal/execute/build/host.go index 91f50aa59c..ab694c47e3 100644 --- a/internal/execute/build/host.go +++ b/internal/execute/build/host.go @@ -6,8 +6,10 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/execute/incremental" "github.com/microsoft/typescript-go/internal/execute/tsc" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" @@ -37,6 +39,10 @@ func (h *host) FS() vfs.FS { return h.host.FS() } +func (h *host) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { + return h.host.MakeResolver(host, options, typingsLocation, projectName) +} + func (h *host) DefaultLibraryPath() string { return h.host.DefaultLibraryPath() } diff --git a/internal/module/resolver.go b/internal/module/resolver.go index 780f0deb67..72cf42cf8d 100644 --- a/internal/module/resolver.go +++ b/internal/module/resolver.go @@ -142,6 +142,13 @@ func GetCompilerOptionsWithRedirect(compilerOptions *core.CompilerOptions, redir return compilerOptions } +type ResolverInterface interface { + ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference ResolvedProjectReference) (*ResolvedModule, []string) + ResolveTypeReferenceDirective(typeReferenceDirectiveName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference ResolvedProjectReference) (*ResolvedTypeReferenceDirective, []string) + GetPackageJsonScopeIfApplicable(path string) *packagejson.InfoCacheEntry + GetPackageScopeForPath(directory string) *packagejson.InfoCacheEntry +} + type Resolver struct { caches host ResolutionHost diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index 36cf2dfc4c..8d3a77b5d0 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -6,7 +6,9 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/ls" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/project/logging" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" @@ -209,3 +211,7 @@ func (fs *compilerFS) Remove(path string) error { func (fs *compilerFS) Chtimes(path string, atime time.Time, mtime time.Time) error { panic("unimplemented") } + +func (c *compilerHost) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { + return module.NewResolver(host, options, typingsLocation, projectName) +} From e397aae411cd07533f9871b311ba7353ecd2843d Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 17 Sep 2025 15:12:25 -0700 Subject: [PATCH 02/28] refactor out session / project host --- internal/api/server.go | 1 + internal/ls/api.go | 86 ++++++++++++++++++++ internal/lsp/server.go | 1 + internal/project/compilerhost.go | 39 ++++++++- internal/project/project.go | 6 +- internal/project/projectcollectionbuilder.go | 9 +- internal/project/session.go | 4 + internal/project/snapshot.go | 5 +- internal/project/snapshot_test.go | 2 +- 9 files changed, 143 insertions(+), 10 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index 5a3ce4213b..9d4f106e32 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -111,6 +111,7 @@ func NewServer(options *ServerOptions) *Server { DefaultLibraryPath: options.DefaultLibraryPath, PositionEncoding: lsproto.PositionEncodingKindUTF8, LoggingEnabled: true, + MakeHost: project.NewProjectHost, }, }) return server diff --git a/internal/ls/api.go b/internal/ls/api.go index 4393806e72..993671ed62 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "slices" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/astnav" @@ -42,3 +43,88 @@ func (l *LanguageService) GetTypeOfSymbol(ctx context.Context, symbol *ast.Symbo defer done() return checker.GetTypeOfSymbolAtLocation(symbol, nil) } + +type DiagnosticId uint32 + +type Diagnostic struct { + Id DiagnosticId + FileName string + Pos int32 + End int32 + Code int32 + Category string + Message string + MessageChain []DiagnosticId + RelatedInformation []DiagnosticId + ReportsUnnecessary bool + ReportsDeprecated bool + SkippedOnNoEmit bool +} + +type diagnosticMaps struct { + diagnosticMapById map[DiagnosticId]*Diagnostic + diagnosticReverseMap map[*ast.Diagnostic]DiagnosticId +} + +func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic) DiagnosticId { + if i, ok := d.diagnosticReverseMap[diagnostic]; ok { + return i + } + id := DiagnosticId(len(d.diagnosticMapById) + 1) + + diag := &Diagnostic{ + Id: id, + FileName: diagnostic.File().FileName(), + Pos: int32(diagnostic.Loc().Pos()), + End: int32(diagnostic.Loc().End()), + Code: diagnostic.Code(), + Category: diagnostic.Category().Name(), + Message: diagnostic.Message(), + MessageChain: make([]DiagnosticId, 0, len(diagnostic.MessageChain())), + RelatedInformation: make([]DiagnosticId, 0, len(diagnostic.RelatedInformation())), + } + + d.diagnosticReverseMap[diagnostic] = id + + for _, messageChain := range diagnostic.MessageChain() { + diag.MessageChain = append(diag.MessageChain, d.addDiagnostic(messageChain)) + } + + for _, relatedInformation := range diagnostic.RelatedInformation() { + diag.RelatedInformation = append(diag.RelatedInformation, d.addDiagnostic(relatedInformation)) + } + + d.diagnosticMapById[id] = diag + return id +} + +func (d *diagnosticMaps) GetDiagnostics() []*Diagnostic { + diagnostics := make([]*Diagnostic, 0, len(d.diagnosticMapById)) + for _, diagnostic := range d.diagnosticMapById { + diagnostics = append(diagnostics, diagnostic) + } + + slices.SortFunc(diagnostics, func(a, b *Diagnostic) int { + return int(int64(a.Id) - int64(b.Id)) + }) + return diagnostics +} + +func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { + program := l.GetProgram() + sourceFiles := program.GetSourceFiles() + program.CheckSourceFiles(ctx, sourceFiles) + diagnosticMaps := &diagnosticMaps{ + diagnosticMapById: make(map[DiagnosticId]*Diagnostic), + diagnosticReverseMap: make(map[*ast.Diagnostic]DiagnosticId), + } + for _, sourceFile := range sourceFiles { + for _, diagnostic := range program.GetSyntacticDiagnostics(ctx, sourceFile) { + diagnosticMaps.addDiagnostic(diagnostic) + } + for _, diagnostic := range program.GetSemanticDiagnostics(ctx, sourceFile) { + diagnosticMaps.addDiagnostic(diagnostic) + } + } + return diagnosticMaps.GetDiagnostics() +} diff --git a/internal/lsp/server.go b/internal/lsp/server.go index 165b0972db..de2e22e27b 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -648,6 +648,7 @@ func (s *Server) handleInitialized(ctx context.Context, params *lsproto.Initiali WatchEnabled: s.watchEnabled, LoggingEnabled: true, DebounceDelay: 500 * time.Millisecond, + MakeHost: project.NewProjectHost, }, FS: s.fs, Logger: s.logger, diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index 8d3a77b5d0..f74ee78dc9 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -15,7 +15,18 @@ import ( "github.com/microsoft/typescript-go/internal/vfs" ) +type ProjectHost interface { + compiler.CompilerHost + Builder() *projectCollectionBuilder + SessionOptions() *SessionOptions + SeenFiles() *collections.SyncSet[tspath.Path] + UpdateSeenFiles(*collections.SyncSet[tspath.Path]) + Freeze(snapshotFS *snapshotFS, configFileRegistry *ConfigFileRegistry) + CompilerFS() *compilerFS +} + var _ compiler.CompilerHost = (*compilerHost)(nil) +var _ ProjectHost = (*compilerHost)(nil) type compilerHost struct { configFilePath tspath.Path @@ -47,12 +58,12 @@ func (c *builderFileSource) FS() vfs.FS { return c.snapshotFSBuilder.FS() } -func newCompilerHost( +func NewProjectHost( currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree, -) *compilerHost { +) ProjectHost { seenFiles := &collections.SyncSet[tspath.Path]{} compilerFS := &compilerFS{ source: &builderFileSource{ @@ -215,3 +226,27 @@ func (fs *compilerFS) Chtimes(path string, atime time.Time, mtime time.Time) err func (c *compilerHost) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { return module.NewResolver(host, options, typingsLocation, projectName) } + +func (c *compilerHost) Builder() *projectCollectionBuilder { + return c.builder +} + +func (c *compilerHost) SessionOptions() *SessionOptions { + return c.sessionOptions +} + +func (c *compilerHost) SeenFiles() *collections.SyncSet[tspath.Path] { + return c.seenFiles +} + +func (c *compilerHost) UpdateSeenFiles(seenFiles *collections.SyncSet[tspath.Path]) { + c.seenFiles = seenFiles +} + +func (c *compilerHost) Freeze(snapshotFS *snapshotFS, configFileRegistry *ConfigFileRegistry) { + c.freeze(snapshotFS, configFileRegistry) +} + +func (c *compilerHost) CompilerFS() *compilerFS { + return c.compilerFS +} \ No newline at end of file diff --git a/internal/project/project.go b/internal/project/project.go index 6354e0ab30..3b8df2c51b 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -62,7 +62,7 @@ type Project struct { dirty bool dirtyFilePath tspath.Path - host *compilerHost + host ProjectHost CommandLine *tsoptions.ParsedCommandLine commandLineWithTypingsFiles *tsoptions.ParsedCommandLine commandLineWithTypingsFilesOnce sync.Once @@ -294,14 +294,14 @@ func (p *Project) CreateProgram() CreateProgramResult { if file.Path() != p.dirtyFilePath { // UpdateProgram only called host.GetSourceFile for the dirty file. // Increment ref count for all other files. - p.host.builder.parseCache.Ref(file) + p.host.Builder().parseCache.Ref(file) } } } } else { var typingsLocation string if p.GetTypeAcquisition().Enable.IsTrue() { - typingsLocation = p.host.sessionOptions.TypingsLocation + typingsLocation = p.host.SessionOptions().TypingsLocation } newProgram = compiler.NewProgram( compiler.ProgramOptions{ diff --git a/internal/project/projectcollectionbuilder.go b/internal/project/projectcollectionbuilder.go index 7717016908..5ddbe820c8 100644 --- a/internal/project/projectcollectionbuilder.go +++ b/internal/project/projectcollectionbuilder.go @@ -27,6 +27,8 @@ const ( type projectCollectionBuilder struct { sessionOptions *SessionOptions + makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost + host ProjectHost parseCache *ParseCache extendedConfigCache *extendedConfigCache @@ -56,6 +58,7 @@ func newProjectCollectionBuilder( sessionOptions *SessionOptions, parseCache *ParseCache, extendedConfigCache *extendedConfigCache, + makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost, ) *projectCollectionBuilder { return &projectCollectionBuilder{ ctx: ctx, @@ -64,6 +67,7 @@ func newProjectCollectionBuilder( sessionOptions: sessionOptions, parseCache: parseCache, extendedConfigCache: extendedConfigCache, + makeHost: makeHost, base: oldProjectCollection, configFileRegistryBuilder: newConfigFileRegistryBuilder(fs, oldConfigFileRegistry, extendedConfigCache, sessionOptions, nil), newSnapshotID: newSnapshotID, @@ -690,6 +694,7 @@ func (b *projectCollectionBuilder) findOrCreateProject( configFilePath tspath.Path, loadKind projectLoadKind, logger *logging.LogTree, + ) *dirty.SyncMapEntry[tspath.Path, *Project] { if loadKind == projectLoadKindFind { entry, _ := b.configuredProjects.Load(configFilePath) @@ -781,14 +786,14 @@ func (b *projectCollectionBuilder) updateProgram(entry dirty.Value[*Project], lo if updateProgram { entry.Change(func(project *Project) { oldHost := project.host - project.host = newCompilerHost(project.currentDirectory, project, b, logger.Fork("CompilerHost")) + project.host = NewProjectHost(project.currentDirectory, project, b, logger.Fork("CompilerHost")) result := project.CreateProgram() project.Program = result.Program project.checkerPool = result.CheckerPool project.ProgramUpdateKind = result.UpdateKind project.ProgramLastUpdate = b.newSnapshotID if result.UpdateKind == ProgramUpdateKindCloned { - project.host.seenFiles = oldHost.seenFiles + project.host.UpdateSeenFiles(oldHost.SeenFiles()) } if result.UpdateKind == ProgramUpdateKindNewFiles { filesChanged = true diff --git a/internal/project/session.go b/internal/project/session.go index 4e78c28884..6908fa54bb 100644 --- a/internal/project/session.go +++ b/internal/project/session.go @@ -43,6 +43,7 @@ type SessionOptions struct { WatchEnabled bool LoggingEnabled bool DebounceDelay time.Duration + MakeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost } type SessionInit struct { @@ -108,6 +109,8 @@ type Session struct { // after file watch changes and ATA updates. diagnosticsRefreshCancel context.CancelFunc diagnosticsRefreshMu sync.Mutex + + makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost } func NewSession(init *SessionInit) *Session { @@ -149,6 +152,7 @@ func NewSession(init *SessionInit) *Session { toPath, ), pendingATAChanges: make(map[tspath.Path]*ATAStateChange), + makeHost: init.Options.MakeHost, } if init.Options.TypingsLocation != "" && init.NpmExecutor != nil { diff --git a/internal/project/snapshot.go b/internal/project/snapshot.go index b5d7d1722e..fda758ceb1 100644 --- a/internal/project/snapshot.go +++ b/internal/project/snapshot.go @@ -174,6 +174,7 @@ func (s *Snapshot) Clone(ctx context.Context, change SnapshotChange, overlays ma s.sessionOptions, session.parseCache, session.extendedConfigCache, + session.makeHost, ) var apiError error @@ -211,7 +212,7 @@ func (s *Snapshot) Clone(ctx context.Context, change SnapshotChange, overlays ma removedFiles := 0 fs.diskFiles.Range(func(entry *dirty.SyncMapEntry[tspath.Path, *diskFile]) bool { for _, project := range projectCollection.Projects() { - if project.host.seenFiles.Has(entry.Key()) { + if project.host.SeenFiles().Has(entry.Key()) { return true } } @@ -257,7 +258,7 @@ func (s *Snapshot) Clone(ctx context.Context, change SnapshotChange, overlays ma // mutations don't happen afterwards. In the future, we might improve things by // separating what it takes to build a program from what it takes to use a program, // and only pass the former into NewProgram instead of retaining it indefinitely. - project.host.freeze(snapshotFS, newSnapshot.ConfigFileRegistry) + project.host.Freeze(snapshotFS, newSnapshot.ConfigFileRegistry) } } for path, config := range newSnapshot.ConfigFileRegistry.configs { diff --git a/internal/project/snapshot_test.go b/internal/project/snapshot_test.go index a075039b33..9ae7aa3366 100644 --- a/internal/project/snapshot_test.go +++ b/internal/project/snapshot_test.go @@ -67,7 +67,7 @@ func TestSnapshot(t *testing.T) { assert.Equal(t, snapshotBefore.ProjectCollection.InferredProject(), snapshotAfter.ProjectCollection.InferredProject()) assert.Equal(t, snapshotAfter.ProjectCollection.InferredProject().ProgramUpdateKind, ProgramUpdateKindNewFiles) // host for inferred project should not change - assert.Equal(t, snapshotAfter.ProjectCollection.InferredProject().host.compilerFS.source, snapshotBefore.fs) + assert.Equal(t, snapshotAfter.ProjectCollection.InferredProject().host.CompilerFS().source, snapshotBefore.fs) }) t.Run("cached disk files are cleaned up", func(t *testing.T) { From 6b5759ec7507634be1c65c9756d453a119acd965 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 17 Sep 2025 18:18:10 -0700 Subject: [PATCH 03/28] init wrapper host --- internal/api/server.go | 82 +++++++++++++++++++- internal/project/compilerhost.go | 48 ++++++------ internal/project/project.go | 6 +- internal/project/projectcollectionbuilder.go | 46 +++++------ internal/project/session.go | 6 +- internal/project/snapshot.go | 4 +- internal/project/snapshotfs.go | 12 +-- 7 files changed, 142 insertions(+), 62 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index 9d4f106e32..27c5f6bd85 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -12,11 +12,16 @@ import ( "time" "github.com/go-json-experiment/json" + "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/project" "github.com/microsoft/typescript-go/internal/project/logging" + "github.com/microsoft/typescript-go/internal/tsoptions" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "github.com/microsoft/typescript-go/internal/vfs/osvfs" ) @@ -88,6 +93,81 @@ type Server struct { requestId int } +type hostWrapper struct { + inner project.ProjectHost +} +// CompilerFS implements project.ProjectHost. +func (h *hostWrapper) CompilerFS() *project.CompilerFS { + return h.inner.CompilerFS() +} + +// DefaultLibraryPath implements project.ProjectHost. +func (h *hostWrapper) DefaultLibraryPath() string { + return h.inner.DefaultLibraryPath() +} + +// FS implements project.ProjectHost. +func (h *hostWrapper) FS() vfs.FS { + return h.inner.FS() +} + +// Freeze implements project.ProjectHost. +func (h *hostWrapper) Freeze(snapshotFS *project.SnapshotFS, configFileRegistry *project.ConfigFileRegistry) { + h.inner.Freeze(snapshotFS, configFileRegistry) +} + +// GetCurrentDirectory implements project.ProjectHost. +func (h *hostWrapper) GetCurrentDirectory() string { + return h.inner.GetCurrentDirectory() +} + +// GetResolvedProjectReference implements project.ProjectHost. +func (h *hostWrapper) GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine { + return h.inner.GetResolvedProjectReference(fileName, path) +} + +// GetSourceFile implements project.ProjectHost. +func (h *hostWrapper) GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile { + return h.inner.GetSourceFile(opts) +} + +// MakeResolver implements project.ProjectHost. +func (h *hostWrapper) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { + return h.inner.MakeResolver(host, options, typingsLocation, projectName) +} + +// SeenFiles implements project.ProjectHost. +func (h *hostWrapper) SeenFiles() *collections.SyncSet[tspath.Path] { + return h.inner.SeenFiles() +} + +// Trace implements project.ProjectHost. +func (h *hostWrapper) Trace(msg string) { + h.inner.Trace(msg) +} + +// UpdateSeenFiles implements project.ProjectHost. +func (h *hostWrapper) UpdateSeenFiles(seenFiles *collections.SyncSet[tspath.Path]) { + h.inner.UpdateSeenFiles(seenFiles) +} + +var _ project.ProjectHost = (*hostWrapper)(nil) + +func (h *hostWrapper) Builder() *project.ProjectCollectionBuilder { + return h.inner.Builder() +} + +func (h *hostWrapper) SessionOptions() *project.SessionOptions { + return h.inner.SessionOptions() +} + +func newProjectHostWrapper(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree) project.ProjectHost { + inner := project.NewProjectHost(currentDirectory, proj, builder, logger) + return &hostWrapper{ + inner: inner, + } +} + func NewServer(options *ServerOptions) *Server { if options.Cwd == "" { panic("Cwd is required") @@ -111,7 +191,7 @@ func NewServer(options *ServerOptions) *Server { DefaultLibraryPath: options.DefaultLibraryPath, PositionEncoding: lsproto.PositionEncodingKindUTF8, LoggingEnabled: true, - MakeHost: project.NewProjectHost, + MakeHost: newProjectHostWrapper, }, }) return server diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index f74ee78dc9..8b8be755bd 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -17,12 +17,12 @@ import ( type ProjectHost interface { compiler.CompilerHost - Builder() *projectCollectionBuilder + Builder() *ProjectCollectionBuilder SessionOptions() *SessionOptions SeenFiles() *collections.SyncSet[tspath.Path] UpdateSeenFiles(*collections.SyncSet[tspath.Path]) - Freeze(snapshotFS *snapshotFS, configFileRegistry *ConfigFileRegistry) - CompilerFS() *compilerFS + Freeze(snapshotFS *SnapshotFS, configFileRegistry *ConfigFileRegistry) + CompilerFS() *CompilerFS } var _ compiler.CompilerHost = (*compilerHost)(nil) @@ -34,12 +34,12 @@ type compilerHost struct { sessionOptions *SessionOptions fs *snapshotFSBuilder - compilerFS *compilerFS + compilerFS *CompilerFS configFileRegistry *ConfigFileRegistry seenFiles *collections.SyncSet[tspath.Path] project *Project - builder *projectCollectionBuilder + builder *ProjectCollectionBuilder logger *logging.LogTree } @@ -61,11 +61,11 @@ func (c *builderFileSource) FS() vfs.FS { func NewProjectHost( currentDirectory string, project *Project, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, ) ProjectHost { seenFiles := &collections.SyncSet[tspath.Path]{} - compilerFS := &compilerFS{ + compilerFS := &CompilerFS{ source: &builderFileSource{ seenFiles: seenFiles, snapshotFSBuilder: builder.fs, @@ -89,7 +89,7 @@ func NewProjectHost( // freeze clears references to mutable state to make the compilerHost safe for use // after the snapshot has been finalized. See the usage in snapshot.go for more details. -func (c *compilerHost) freeze(snapshotFS *snapshotFS, configFileRegistry *ConfigFileRegistry) { +func (c *compilerHost) freeze(snapshotFS *SnapshotFS, configFileRegistry *ConfigFileRegistry) { if c.builder == nil { panic("freeze can only be called once") } @@ -156,19 +156,19 @@ func (c *compilerHost) Trace(msg string) { panic("unimplemented") } -var _ vfs.FS = (*compilerFS)(nil) +var _ vfs.FS = (*CompilerFS)(nil) -type compilerFS struct { +type CompilerFS struct { source FileSource } // DirectoryExists implements vfs.FS. -func (fs *compilerFS) DirectoryExists(path string) bool { +func (fs *CompilerFS) DirectoryExists(path string) bool { return fs.source.FS().DirectoryExists(path) } // FileExists implements vfs.FS. -func (fs *compilerFS) FileExists(path string) bool { +func (fs *CompilerFS) FileExists(path string) bool { if fh := fs.source.GetFile(path); fh != nil { return true } @@ -176,12 +176,12 @@ func (fs *compilerFS) FileExists(path string) bool { } // GetAccessibleEntries implements vfs.FS. -func (fs *compilerFS) GetAccessibleEntries(path string) vfs.Entries { +func (fs *CompilerFS) GetAccessibleEntries(path string) vfs.Entries { return fs.source.FS().GetAccessibleEntries(path) } // ReadFile implements vfs.FS. -func (fs *compilerFS) ReadFile(path string) (contents string, ok bool) { +func (fs *CompilerFS) ReadFile(path string) (contents string, ok bool) { if fh := fs.source.GetFile(path); fh != nil { return fh.Content(), true } @@ -189,37 +189,37 @@ func (fs *compilerFS) ReadFile(path string) (contents string, ok bool) { } // Realpath implements vfs.FS. -func (fs *compilerFS) Realpath(path string) string { +func (fs *CompilerFS) Realpath(path string) string { return fs.source.FS().Realpath(path) } // Stat implements vfs.FS. -func (fs *compilerFS) Stat(path string) vfs.FileInfo { +func (fs *CompilerFS) Stat(path string) vfs.FileInfo { return fs.source.FS().Stat(path) } // UseCaseSensitiveFileNames implements vfs.FS. -func (fs *compilerFS) UseCaseSensitiveFileNames() bool { +func (fs *CompilerFS) UseCaseSensitiveFileNames() bool { return fs.source.FS().UseCaseSensitiveFileNames() } // WalkDir implements vfs.FS. -func (fs *compilerFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { +func (fs *CompilerFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { panic("unimplemented") } // WriteFile implements vfs.FS. -func (fs *compilerFS) WriteFile(path string, data string, writeByteOrderMark bool) error { +func (fs *CompilerFS) WriteFile(path string, data string, writeByteOrderMark bool) error { panic("unimplemented") } // Remove implements vfs.FS. -func (fs *compilerFS) Remove(path string) error { +func (fs *CompilerFS) Remove(path string) error { panic("unimplemented") } // Chtimes implements vfs.FS. -func (fs *compilerFS) Chtimes(path string, atime time.Time, mtime time.Time) error { +func (fs *CompilerFS) Chtimes(path string, atime time.Time, mtime time.Time) error { panic("unimplemented") } @@ -227,7 +227,7 @@ func (c *compilerHost) MakeResolver(host module.ResolutionHost, options *core.Co return module.NewResolver(host, options, typingsLocation, projectName) } -func (c *compilerHost) Builder() *projectCollectionBuilder { +func (c *compilerHost) Builder() *ProjectCollectionBuilder { return c.builder } @@ -243,10 +243,10 @@ func (c *compilerHost) UpdateSeenFiles(seenFiles *collections.SyncSet[tspath.Pat c.seenFiles = seenFiles } -func (c *compilerHost) Freeze(snapshotFS *snapshotFS, configFileRegistry *ConfigFileRegistry) { +func (c *compilerHost) Freeze(snapshotFS *SnapshotFS, configFileRegistry *ConfigFileRegistry) { c.freeze(snapshotFS, configFileRegistry) } -func (c *compilerHost) CompilerFS() *compilerFS { +func (c *compilerHost) CompilerFS() *CompilerFS { return c.compilerFS } \ No newline at end of file diff --git a/internal/project/project.go b/internal/project/project.go index 3b8df2c51b..4d96544a64 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -89,7 +89,7 @@ type Project struct { func NewConfiguredProject( configFileName string, configFilePath tspath.Path, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, ) *Project { return NewProject(configFileName, KindConfigured, tspath.GetDirectoryPath(configFileName), builder, logger) @@ -99,7 +99,7 @@ func NewInferredProject( currentDirectory string, compilerOptions *core.CompilerOptions, rootFileNames []string, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, ) *Project { p := NewProject(inferredProjectName, KindInferred, currentDirectory, builder, logger) @@ -134,7 +134,7 @@ func NewProject( configFileName string, kind Kind, currentDirectory string, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, ) *Project { if logger != nil { diff --git a/internal/project/projectcollectionbuilder.go b/internal/project/projectcollectionbuilder.go index 5ddbe820c8..821fcfba44 100644 --- a/internal/project/projectcollectionbuilder.go +++ b/internal/project/projectcollectionbuilder.go @@ -25,9 +25,9 @@ const ( projectLoadKindCreate ) -type projectCollectionBuilder struct { +type ProjectCollectionBuilder struct { sessionOptions *SessionOptions - makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost + makeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost host ProjectHost parseCache *ParseCache extendedConfigCache *extendedConfigCache @@ -58,9 +58,9 @@ func newProjectCollectionBuilder( sessionOptions *SessionOptions, parseCache *ParseCache, extendedConfigCache *extendedConfigCache, - makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost, -) *projectCollectionBuilder { - return &projectCollectionBuilder{ + makeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost, +) *ProjectCollectionBuilder { + return &ProjectCollectionBuilder{ ctx: ctx, fs: fs, compilerOptionsForInferredProjects: compilerOptionsForInferredProjects, @@ -77,7 +77,7 @@ func newProjectCollectionBuilder( } } -func (b *projectCollectionBuilder) Finalize(logger *logging.LogTree) (*ProjectCollection, *ConfigFileRegistry) { +func (b *ProjectCollectionBuilder) Finalize(logger *logging.LogTree) (*ProjectCollection, *ConfigFileRegistry) { var changed bool newProjectCollection := b.base ensureCloned := func() { @@ -107,7 +107,7 @@ func (b *projectCollectionBuilder) Finalize(logger *logging.LogTree) (*ProjectCo return newProjectCollection, configFileRegistry } -func (b *projectCollectionBuilder) forEachProject(fn func(entry dirty.Value[*Project]) bool) { +func (b *ProjectCollectionBuilder) forEachProject(fn func(entry dirty.Value[*Project]) bool) { keepGoing := true b.configuredProjects.Range(func(entry *dirty.SyncMapEntry[tspath.Path, *Project]) bool { keepGoing = fn(entry) @@ -121,7 +121,7 @@ func (b *projectCollectionBuilder) forEachProject(fn func(entry dirty.Value[*Pro } } -func (b *projectCollectionBuilder) HandleAPIRequest(apiRequest *APISnapshotRequest, logger *logging.LogTree) error { +func (b *ProjectCollectionBuilder) HandleAPIRequest(apiRequest *APISnapshotRequest, logger *logging.LogTree) error { var projectsToClose map[tspath.Path]struct{} if apiRequest.CloseProjects != nil { projectsToClose = maps.Clone(apiRequest.CloseProjects.M) @@ -170,7 +170,7 @@ func (b *projectCollectionBuilder) HandleAPIRequest(apiRequest *APISnapshotReque return nil } -func (b *projectCollectionBuilder) DidChangeFiles(summary FileChangeSummary, logger *logging.LogTree) { +func (b *ProjectCollectionBuilder) DidChangeFiles(summary FileChangeSummary, logger *logging.LogTree) { changedFiles := make([]tspath.Path, 0, len(summary.Closed)+summary.Changed.Len()) for uri, hash := range summary.Closed { fileName := uri.FileName() @@ -279,7 +279,7 @@ func logChangeFileResult(result changeFileResult, logger *logging.LogTree) { } } -func (b *projectCollectionBuilder) DidRequestFile(uri lsproto.DocumentUri, logger *logging.LogTree) { +func (b *ProjectCollectionBuilder) DidRequestFile(uri lsproto.DocumentUri, logger *logging.LogTree) { startTime := time.Now() fileName := uri.FileName() hasChanges := b.programStructureChanged @@ -330,7 +330,7 @@ func (b *projectCollectionBuilder) DidRequestFile(uri lsproto.DocumentUri, logge } } -func (b *projectCollectionBuilder) DidUpdateATAState(ataChanges map[tspath.Path]*ATAStateChange, logger *logging.LogTree) { +func (b *ProjectCollectionBuilder) DidUpdateATAState(ataChanges map[tspath.Path]*ATAStateChange, logger *logging.LogTree) { updateProject := func(project dirty.Value[*Project], ataChange *ATAStateChange) { project.ChangeIf( func(p *Project) bool { @@ -375,7 +375,7 @@ func (b *projectCollectionBuilder) DidUpdateATAState(ataChanges map[tspath.Path] } } -func (b *projectCollectionBuilder) markProjectsAffectedByConfigChanges( +func (b *ProjectCollectionBuilder) markProjectsAffectedByConfigChanges( configChangeResult changeFileResult, logger *logging.LogTree, ) bool { @@ -407,7 +407,7 @@ func (b *projectCollectionBuilder) markProjectsAffectedByConfigChanges( return hasChanges } -func (b *projectCollectionBuilder) findDefaultProject(fileName string, path tspath.Path) dirty.Value[*Project] { +func (b *ProjectCollectionBuilder) findDefaultProject(fileName string, path tspath.Path) dirty.Value[*Project] { if configuredProject := b.findDefaultConfiguredProject(fileName, path); configuredProject != nil { return configuredProject } @@ -424,7 +424,7 @@ func (b *projectCollectionBuilder) findDefaultProject(fileName string, path tspa return nil } -func (b *projectCollectionBuilder) findDefaultConfiguredProject(fileName string, path tspath.Path) *dirty.SyncMapEntry[tspath.Path, *Project] { +func (b *ProjectCollectionBuilder) findDefaultConfiguredProject(fileName string, path tspath.Path) *dirty.SyncMapEntry[tspath.Path, *Project] { // !!! look in fileDefaultProjects first? // Sort configured projects so we can use a deterministic "first" as a last resort. var configuredProjectPaths []tspath.Path @@ -449,7 +449,7 @@ func (b *projectCollectionBuilder) findDefaultConfiguredProject(fileName string, return configuredProjects[project] } -func (b *projectCollectionBuilder) ensureConfiguredProjectAndAncestorsForOpenFile(fileName string, path tspath.Path, logger *logging.LogTree) searchResult { +func (b *ProjectCollectionBuilder) ensureConfiguredProjectAndAncestorsForOpenFile(fileName string, path tspath.Path, logger *logging.LogTree) searchResult { result := b.findOrCreateDefaultConfiguredProjectForOpenScriptInfo(fileName, path, projectLoadKindCreate, logger) if result.project != nil { // !!! sheetal todo this later @@ -487,7 +487,7 @@ type searchResult struct { retain collections.Set[tspath.Path] } -func (b *projectCollectionBuilder) findOrCreateDefaultConfiguredProjectWorker( +func (b *ProjectCollectionBuilder) findOrCreateDefaultConfiguredProjectWorker( fileName string, path tspath.Path, configFileName string, @@ -645,7 +645,7 @@ func (b *projectCollectionBuilder) findOrCreateDefaultConfiguredProjectWorker( return searchResult{retain: retain} } -func (b *projectCollectionBuilder) findOrCreateDefaultConfiguredProjectForOpenScriptInfo( +func (b *ProjectCollectionBuilder) findOrCreateDefaultConfiguredProjectForOpenScriptInfo( fileName string, path tspath.Path, loadKind projectLoadKind, @@ -689,7 +689,7 @@ func (b *projectCollectionBuilder) findOrCreateDefaultConfiguredProjectForOpenSc return searchResult{} } -func (b *projectCollectionBuilder) findOrCreateProject( +func (b *ProjectCollectionBuilder) findOrCreateProject( configFileName string, configFilePath tspath.Path, loadKind projectLoadKind, @@ -704,11 +704,11 @@ func (b *projectCollectionBuilder) findOrCreateProject( return entry } -func (b *projectCollectionBuilder) toPath(fileName string) tspath.Path { +func (b *ProjectCollectionBuilder) toPath(fileName string) tspath.Path { return tspath.ToPath(fileName, b.sessionOptions.CurrentDirectory, b.fs.fs.UseCaseSensitiveFileNames()) } -func (b *projectCollectionBuilder) updateInferredProjectRoots(rootFileNames []string, logger *logging.LogTree) bool { +func (b *ProjectCollectionBuilder) updateInferredProjectRoots(rootFileNames []string, logger *logging.LogTree) bool { if len(rootFileNames) == 0 { if b.inferredProject.Value() != nil { if logger != nil { @@ -754,7 +754,7 @@ func (b *projectCollectionBuilder) updateInferredProjectRoots(rootFileNames []st // updateProgram updates the program for the given project entry if necessary. It returns // a boolean indicating whether the update could have caused any structure-affecting changes. -func (b *projectCollectionBuilder) updateProgram(entry dirty.Value[*Project], logger *logging.LogTree) bool { +func (b *ProjectCollectionBuilder) updateProgram(entry dirty.Value[*Project], logger *logging.LogTree) bool { var updateProgram bool var filesChanged bool configFileName := entry.Value().configFileName @@ -815,7 +815,7 @@ func (b *projectCollectionBuilder) updateProgram(entry dirty.Value[*Project], lo return filesChanged } -func (b *projectCollectionBuilder) markFilesChanged(entry dirty.Value[*Project], paths []tspath.Path, changeType lsproto.FileChangeType, logger *logging.LogTree) { +func (b *ProjectCollectionBuilder) markFilesChanged(entry dirty.Value[*Project], paths []tspath.Path, changeType lsproto.FileChangeType, logger *logging.LogTree) { var dirty bool var dirtyFilePath tspath.Path entry.ChangeIf( @@ -867,7 +867,7 @@ func (b *projectCollectionBuilder) markFilesChanged(entry dirty.Value[*Project], ) } -func (b *projectCollectionBuilder) deleteConfiguredProject(project dirty.Value[*Project], logger *logging.LogTree) { +func (b *ProjectCollectionBuilder) deleteConfiguredProject(project dirty.Value[*Project], logger *logging.LogTree) { projectPath := project.Value().configFilePath if logger != nil { logger.Log("Deleting configured project: " + project.Value().configFileName) diff --git a/internal/project/session.go b/internal/project/session.go index 6908fa54bb..7304b81752 100644 --- a/internal/project/session.go +++ b/internal/project/session.go @@ -43,7 +43,7 @@ type SessionOptions struct { WatchEnabled bool LoggingEnabled bool DebounceDelay time.Duration - MakeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost + MakeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost } type SessionInit struct { @@ -110,7 +110,7 @@ type Session struct { diagnosticsRefreshCancel context.CancelFunc diagnosticsRefreshMu sync.Mutex - makeHost func(currentDirectory string, project *Project, builder *projectCollectionBuilder, logger *logging.LogTree) ProjectHost + makeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost } func NewSession(init *SessionInit) *Session { @@ -140,7 +140,7 @@ func NewSession(init *SessionInit) *Session { snapshotID: atomic.Uint64{}, snapshot: NewSnapshot( uint64(0), - &snapshotFS{ + &SnapshotFS{ toPath: toPath, fs: init.FS, }, diff --git a/internal/project/snapshot.go b/internal/project/snapshot.go index fda758ceb1..c372800c3d 100644 --- a/internal/project/snapshot.go +++ b/internal/project/snapshot.go @@ -28,7 +28,7 @@ type Snapshot struct { converters *ls.Converters // Immutable state, cloned between snapshots - fs *snapshotFS + fs *SnapshotFS ProjectCollection *ProjectCollection ConfigFileRegistry *ConfigFileRegistry compilerOptionsForInferredProjects *core.CompilerOptions @@ -40,7 +40,7 @@ type Snapshot struct { // NewSnapshot func NewSnapshot( id uint64, - fs *snapshotFS, + fs *SnapshotFS, sessionOptions *SessionOptions, parseCache *ParseCache, extendedConfigCache *extendedConfigCache, diff --git a/internal/project/snapshotfs.go b/internal/project/snapshotfs.go index 29b51a1dfe..0302f55505 100644 --- a/internal/project/snapshotfs.go +++ b/internal/project/snapshotfs.go @@ -16,21 +16,21 @@ type FileSource interface { var ( _ FileSource = (*snapshotFSBuilder)(nil) - _ FileSource = (*snapshotFS)(nil) + _ FileSource = (*SnapshotFS)(nil) ) -type snapshotFS struct { +type SnapshotFS struct { toPath func(fileName string) tspath.Path fs vfs.FS overlays map[tspath.Path]*overlay diskFiles map[tspath.Path]*diskFile } -func (s *snapshotFS) FS() vfs.FS { +func (s *SnapshotFS) FS() vfs.FS { return s.fs } -func (s *snapshotFS) GetFile(fileName string) FileHandle { +func (s *SnapshotFS) GetFile(fileName string) FileHandle { if file, ok := s.overlays[s.toPath(fileName)]; ok { return file } @@ -68,9 +68,9 @@ func (s *snapshotFSBuilder) FS() vfs.FS { return s.fs } -func (s *snapshotFSBuilder) Finalize() (*snapshotFS, bool) { +func (s *snapshotFSBuilder) Finalize() (*SnapshotFS, bool) { diskFiles, changed := s.diskFiles.Finalize() - return &snapshotFS{ + return &SnapshotFS{ fs: s.fs, overlays: s.overlays, diskFiles: diskFiles, From 32f623dbea3e4f45f2bdb41ec010a7e56f0d895e Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 17 Sep 2025 18:27:21 -0700 Subject: [PATCH 04/28] actually use custom host --- internal/project/projectcollectionbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/project/projectcollectionbuilder.go b/internal/project/projectcollectionbuilder.go index 821fcfba44..2485535173 100644 --- a/internal/project/projectcollectionbuilder.go +++ b/internal/project/projectcollectionbuilder.go @@ -786,7 +786,7 @@ func (b *ProjectCollectionBuilder) updateProgram(entry dirty.Value[*Project], lo if updateProgram { entry.Change(func(project *Project) { oldHost := project.host - project.host = NewProjectHost(project.currentDirectory, project, b, logger.Fork("CompilerHost")) + project.host = b.sessionOptions.MakeHost(project.currentDirectory, project, b, logger.Fork("CompilerHost")) result := project.CreateProgram() project.Program = result.Program project.checkerPool = result.CheckerPool From e738efca4b2f420f28d0453d7e9bbb1d68a9899f Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 18 Sep 2025 11:29:06 -0700 Subject: [PATCH 05/28] more wiring for api --- internal/api/api.go | 23 ++++++++ internal/api/log.go | 23 ++++++++ internal/api/proto.go | 6 +++ internal/api/server.go | 8 ++- internal/ls/api.go | 57 ++++++++++---------- internal/project/compilerhost.go | 8 +-- internal/project/project.go | 2 +- internal/project/projectcollectionbuilder.go | 1 - 8 files changed, 92 insertions(+), 36 deletions(-) create mode 100644 internal/api/log.go diff --git a/internal/api/api.go b/internal/api/api.go index 15fc7c096f..4f6dfc320a 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -105,6 +105,9 @@ func (api *API) HandleRequest(ctx context.Context, method string, payload []byte return encodeJSON(core.TryMap(params.Symbols, func(symbol Handle[ast.Symbol]) (any, error) { return api.GetTypeOfSymbol(ctx, params.Project, symbol) })) + case MethodGetDiagnostics: + params := params.(*GetDiagnosticsParams) + return encodeJSON((api.GetDiagnostics(ctx, params.Project))) default: return nil, fmt.Errorf("unhandled API method %q", method) } @@ -262,6 +265,26 @@ func (api *API) GetSourceFile(projectId Handle[project.Project], fileName string return sourceFile, nil } +func (api *API) GetDiagnostics(ctx context.Context, projectId Handle[project.Project]) ([]*ls.Diagnostic, error) { + projectPath, ok := api.projects[projectId] + if !ok { + return nil, errors.New("project ID not found") + } + snapshot, release := api.session.Snapshot() + defer release() + project := snapshot.ProjectCollection.GetProjectByPath(projectPath) + if project == nil { + return nil, errors.New("project not found") + } + + languageService := ls.NewLanguageService(project, snapshot.Converters()) + diagnostics := languageService.GetDiagnostics(ctx) + + api.symbolsMu.Lock() + defer api.symbolsMu.Unlock() + return diagnostics, nil +} + func (api *API) releaseHandle(handle string) error { switch handle[0] { case handlePrefixProject: diff --git a/internal/api/log.go b/internal/api/log.go new file mode 100644 index 0000000000..550b2ccdd2 --- /dev/null +++ b/internal/api/log.go @@ -0,0 +1,23 @@ +package api + +import "github.com/microsoft/typescript-go/internal/project/logging" + +type NoLogger struct{} + +// SetVerbose implements logging.Logger. +func (n NoLogger) SetVerbose(verbose bool) { + panic("unimplemented") +} + +var _ logging.Logger = (*NoLogger)(nil) + +func (n NoLogger) Log(msg ...any) {} +func (n NoLogger) Logf(format string, args ...any) {} +func (n NoLogger) Write(msg string) {} +func (n NoLogger) Verbose() logging.Logger { + return n +} + +func (n NoLogger) IsVerbose() bool { + return false +} diff --git a/internal/api/proto.go b/internal/api/proto.go index e0d5307875..256f21236f 100644 --- a/internal/api/proto.go +++ b/internal/api/proto.go @@ -87,6 +87,7 @@ const ( MethodGetTypeOfSymbol Method = "getTypeOfSymbol" MethodGetTypesOfSymbols Method = "getTypesOfSymbols" MethodGetSourceFile Method = "getSourceFile" + MethodGetDiagnostics Method = "getDiagnostics" ) var unmarshalers = map[Method]func([]byte) (any, error){ @@ -100,6 +101,7 @@ var unmarshalers = map[Method]func([]byte) (any, error){ MethodGetSymbolsAtLocations: unmarshallerFor[GetSymbolsAtLocationsParams], MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams], MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams], + MethodGetDiagnostics: unmarshallerFor[GetDiagnosticsParams], } type ConfigureParams struct { @@ -174,6 +176,10 @@ func NewSymbolResponse(symbol *ast.Symbol) *SymbolResponse { } } +type GetDiagnosticsParams struct { + Project Handle[project.Project] `json:"project"` +} + type GetTypeOfSymbolParams struct { Project Handle[project.Project] `json:"project"` Symbol Handle[ast.Symbol] `json:"symbol"` diff --git a/internal/api/server.go b/internal/api/server.go index 27c5f6bd85..62ac1a1d44 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -8,6 +8,7 @@ import ( "io" "runtime/debug" "strconv" + "strings" "sync" "time" @@ -96,6 +97,7 @@ type Server struct { type hostWrapper struct { inner project.ProjectHost } + // CompilerFS implements project.ProjectHost. func (h *hostWrapper) CompilerFS() *project.CompilerFS { return h.inner.CompilerFS() @@ -181,7 +183,8 @@ func NewServer(options *ServerOptions) *Server { fs: bundled.WrapFS(osvfs.FS()), defaultLibraryPath: options.DefaultLibraryPath, } - logger := logging.NewLogger(options.Err) + // logger := logging.NewLogger(options.Err) + logger := NoLogger{} server.logger = logger server.api = NewAPI(&APIInit{ Logger: logger, @@ -510,7 +513,8 @@ func (s *Server) GetAccessibleEntries(path string) vfs.Entries { // ReadFile implements vfs.FS. func (s *Server) ReadFile(path string) (contents string, ok bool) { - if s.enabledCallbacks&CallbackReadFile != 0 { + if s.enabledCallbacks&CallbackReadFile != 0 && !strings.HasPrefix(path, "bundled://") { + data, err := s.call("readFile", path) if err != nil { panic(err) diff --git a/internal/ls/api.go b/internal/ls/api.go index 993671ed62..ead0c18cc3 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -47,22 +47,22 @@ func (l *LanguageService) GetTypeOfSymbol(ctx context.Context, symbol *ast.Symbo type DiagnosticId uint32 type Diagnostic struct { - Id DiagnosticId - FileName string - Pos int32 - End int32 - Code int32 - Category string - Message string - MessageChain []DiagnosticId - RelatedInformation []DiagnosticId - ReportsUnnecessary bool - ReportsDeprecated bool - SkippedOnNoEmit bool + Id DiagnosticId `json:"id"` + FileName string `json:"fileName"` + Pos int32 `json:"pos"` + End int32 `json:"end"` + Code int32 `json:"code"` + Category string `json:"category"` + Message string `json:"message"` + MessageChain []DiagnosticId `json:"messageChain"` + RelatedInformation []DiagnosticId `json:"relatedInformation"` + ReportsUnnecessary bool `json:"reportsUnnecessary"` + ReportsDeprecated bool `json:"reportsDeprecated"` + SkippedOnNoEmit bool `json:"skippedOnNoEmit"` } type diagnosticMaps struct { - diagnosticMapById map[DiagnosticId]*Diagnostic + diagnosticMapById map[DiagnosticId]*Diagnostic diagnosticReverseMap map[*ast.Diagnostic]DiagnosticId } @@ -73,32 +73,32 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic) DiagnosticId id := DiagnosticId(len(d.diagnosticMapById) + 1) diag := &Diagnostic{ - Id: id, - FileName: diagnostic.File().FileName(), - Pos: int32(diagnostic.Loc().Pos()), - End: int32(diagnostic.Loc().End()), - Code: diagnostic.Code(), - Category: diagnostic.Category().Name(), - Message: diagnostic.Message(), - MessageChain: make([]DiagnosticId, 0, len(diagnostic.MessageChain())), + Id: id, + FileName: diagnostic.File().FileName(), + Pos: int32(diagnostic.Loc().Pos()), + End: int32(diagnostic.Loc().End()), + Code: diagnostic.Code(), + Category: diagnostic.Category().Name(), + Message: diagnostic.Message(), + MessageChain: make([]DiagnosticId, 0, len(diagnostic.MessageChain())), RelatedInformation: make([]DiagnosticId, 0, len(diagnostic.RelatedInformation())), } - + d.diagnosticReverseMap[diagnostic] = id - + for _, messageChain := range diagnostic.MessageChain() { diag.MessageChain = append(diag.MessageChain, d.addDiagnostic(messageChain)) } - + for _, relatedInformation := range diagnostic.RelatedInformation() { diag.RelatedInformation = append(diag.RelatedInformation, d.addDiagnostic(relatedInformation)) } - + d.diagnosticMapById[id] = diag return id } -func (d *diagnosticMaps) GetDiagnostics() []*Diagnostic { +func (d *diagnosticMaps) getDiagnostics() []*Diagnostic { diagnostics := make([]*Diagnostic, 0, len(d.diagnosticMapById)) for _, diagnostic := range d.diagnosticMapById { diagnostics = append(diagnostics, diagnostic) @@ -113,9 +113,8 @@ func (d *diagnosticMaps) GetDiagnostics() []*Diagnostic { func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { program := l.GetProgram() sourceFiles := program.GetSourceFiles() - program.CheckSourceFiles(ctx, sourceFiles) diagnosticMaps := &diagnosticMaps{ - diagnosticMapById: make(map[DiagnosticId]*Diagnostic), + diagnosticMapById: make(map[DiagnosticId]*Diagnostic), diagnosticReverseMap: make(map[*ast.Diagnostic]DiagnosticId), } for _, sourceFile := range sourceFiles { @@ -126,5 +125,5 @@ func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { diagnosticMaps.addDiagnostic(diagnostic) } } - return diagnosticMaps.GetDiagnostics() + return diagnosticMaps.getDiagnostics() } diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index 8b8be755bd..2cb753951f 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -25,8 +25,10 @@ type ProjectHost interface { CompilerFS() *CompilerFS } -var _ compiler.CompilerHost = (*compilerHost)(nil) -var _ ProjectHost = (*compilerHost)(nil) +var ( + _ compiler.CompilerHost = (*compilerHost)(nil) + _ ProjectHost = (*compilerHost)(nil) +) type compilerHost struct { configFilePath tspath.Path @@ -249,4 +251,4 @@ func (c *compilerHost) Freeze(snapshotFS *SnapshotFS, configFileRegistry *Config func (c *compilerHost) CompilerFS() *CompilerFS { return c.compilerFS -} \ No newline at end of file +} diff --git a/internal/project/project.go b/internal/project/project.go index 4d96544a64..cb8d65b4c7 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -300,7 +300,7 @@ func (p *Project) CreateProgram() CreateProgramResult { } } else { var typingsLocation string - if p.GetTypeAcquisition().Enable.IsTrue() { + if acq := p.GetTypeAcquisition(); acq != nil && acq.Enable.IsTrue() { typingsLocation = p.host.SessionOptions().TypingsLocation } newProgram = compiler.NewProgram( diff --git a/internal/project/projectcollectionbuilder.go b/internal/project/projectcollectionbuilder.go index 2485535173..a979415e03 100644 --- a/internal/project/projectcollectionbuilder.go +++ b/internal/project/projectcollectionbuilder.go @@ -694,7 +694,6 @@ func (b *ProjectCollectionBuilder) findOrCreateProject( configFilePath tspath.Path, loadKind projectLoadKind, logger *logging.LogTree, - ) *dirty.SyncMapEntry[tspath.Path, *Project] { if loadKind == projectLoadKindFind { entry, _ := b.configuredProjects.Load(configFilePath) From 6785a0d14992262ab46023f5a6ada2dee79c6e86 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 22 Sep 2025 14:51:58 -0700 Subject: [PATCH 06/28] resolver callbacks --- internal/api/server.go | 127 +++++++++++++++++++++++++++++++++++++++-- internal/ls/api.go | 14 +++-- 2 files changed, 130 insertions(+), 11 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index 62ac1a1d44..1ee18d2261 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -19,6 +19,7 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/lsp/lsproto" "github.com/microsoft/typescript-go/internal/module" + "github.com/microsoft/typescript-go/internal/packagejson" "github.com/microsoft/typescript-go/internal/project" "github.com/microsoft/typescript-go/internal/project/logging" "github.com/microsoft/typescript-go/internal/tsoptions" @@ -64,6 +65,10 @@ const ( CallbackGetAccessibleEntries CallbackReadFile CallbackRealpath + CallbackResolveModuleName + CallbackResolveTypeReferenceDirective + CallbackGetPackageJsonScopeIfApplicable + CallbackGetPackageScopeForPath ) type ServerOptions struct { @@ -95,7 +100,8 @@ type Server struct { } type hostWrapper struct { - inner project.ProjectHost + inner project.ProjectHost + server *Server } // CompilerFS implements project.ProjectHost. @@ -135,7 +141,7 @@ func (h *hostWrapper) GetSourceFile(opts ast.SourceFileParseOptions) *ast.Source // MakeResolver implements project.ProjectHost. func (h *hostWrapper) MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface { - return h.inner.MakeResolver(host, options, typingsLocation, projectName) + return newResolverWrapper(h.inner.MakeResolver(host, options, typingsLocation, projectName), h.server) } // SeenFiles implements project.ProjectHost. @@ -163,13 +169,110 @@ func (h *hostWrapper) SessionOptions() *project.SessionOptions { return h.inner.SessionOptions() } -func newProjectHostWrapper(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree) project.ProjectHost { +func newProjectHostWrapper(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree, server *Server) *hostWrapper { inner := project.NewProjectHost(currentDirectory, proj, builder, logger) return &hostWrapper{ - inner: inner, + inner: inner, + server: server, } } +type resolverWrapper struct { + inner module.ResolverInterface + server *Server +} + +func newResolverWrapper(inner module.ResolverInterface, server *Server) *resolverWrapper { + return &resolverWrapper{ + inner: inner, + server: server, + } +} + +// GetPackageJsonScopeIfApplicable implements module.ResolverInterface. +func (r *resolverWrapper) GetPackageJsonScopeIfApplicable(path string) *packagejson.InfoCacheEntry { + if r.server.CallbackEnabled(CallbackGetPackageJsonScopeIfApplicable) { + result, err := r.server.call("getPackageJsonScopeIfApplicable", path) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res packagejson.InfoCacheEntry + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + return &res + } + } + return r.inner.GetPackageJsonScopeIfApplicable(path) +} + +// GetPackageScopeForPath implements module.ResolverInterface. +func (r *resolverWrapper) GetPackageScopeForPath(directory string) *packagejson.InfoCacheEntry { + if r.server.CallbackEnabled(CallbackGetPackageScopeForPath) { + result, err := r.server.call("getPackageScopeForPath", directory) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res packagejson.InfoCacheEntry + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + return &res + } + } + return r.inner.GetPackageScopeForPath(directory) +} + +// ResolveModuleName implements module.ResolverInterface. +func (r *resolverWrapper) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference module.ResolvedProjectReference) (*module.ResolvedModule, []string) { + if r.server.CallbackEnabled(CallbackResolveModuleName) { + result, err := r.server.call("resolveModuleName", map[string]any{ + "moduleName": moduleName, + "containingFile": containingFile, + "resolutionMode": resolutionMode, + "redirectedReference": redirectedReference, + }) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res module.ResolvedModule + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + return &res, nil + } + } + return r.inner.ResolveModuleName(moduleName, containingFile, resolutionMode, redirectedReference) +} + +// ResolveTypeReferenceDirective implements module.ResolverInterface. +func (r *resolverWrapper) ResolveTypeReferenceDirective(typeReferenceDirectiveName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference module.ResolvedProjectReference) (*module.ResolvedTypeReferenceDirective, []string) { + if r.server.CallbackEnabled(CallbackResolveTypeReferenceDirective) { + result, err := r.server.call("resolveTypeReferenceDirective", map[string]any{ + "typeReferenceDirectiveName": typeReferenceDirectiveName, + "containingFile": containingFile, + "resolutionMode": resolutionMode, + "redirectedReference": redirectedReference, + }) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res module.ResolvedTypeReferenceDirective + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + return &res, nil + } + } + return r.inner.ResolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, resolutionMode, redirectedReference) +} + +var _ module.ResolverInterface = (*resolverWrapper)(nil) + func NewServer(options *ServerOptions) *Server { if options.Cwd == "" { panic("Cwd is required") @@ -194,7 +297,9 @@ func NewServer(options *ServerOptions) *Server { DefaultLibraryPath: options.DefaultLibraryPath, PositionEncoding: lsproto.PositionEncodingKindUTF8, LoggingEnabled: true, - MakeHost: newProjectHostWrapper, + MakeHost: func(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree) project.ProjectHost { + return newProjectHostWrapper(currentDirectory, proj, builder, logger, server) + }, }, }) return server @@ -338,6 +443,14 @@ func (s *Server) enableCallback(callback string) error { s.enabledCallbacks |= CallbackReadFile case "realpath": s.enabledCallbacks |= CallbackRealpath + case "resolveModuleName": + s.enabledCallbacks |= CallbackResolveModuleName + case "resolveTypeReferenceDirective": + s.enabledCallbacks |= CallbackResolveTypeReferenceDirective + case "getPackageJsonScopeIfApplicable": + s.enabledCallbacks |= CallbackGetPackageJsonScopeIfApplicable + case "getPackageScopeForPath": + s.enabledCallbacks |= CallbackGetPackageScopeForPath default: return fmt.Errorf("unknown callback: %s", callback) } @@ -580,3 +693,7 @@ func (s *Server) Remove(path string) error { func (s *Server) Chtimes(path string, aTime time.Time, mTime time.Time) error { panic("unimplemented") } + +func (s *Server) CallbackEnabled(callback Callback) bool { + return s.enabledCallbacks&callback != 0 +} diff --git a/internal/ls/api.go b/internal/ls/api.go index ead0c18cc3..470a880970 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -9,6 +9,7 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/astnav" "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/compiler" ) var ( @@ -117,13 +118,14 @@ func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { diagnosticMapById: make(map[DiagnosticId]*Diagnostic), diagnosticReverseMap: make(map[*ast.Diagnostic]DiagnosticId), } + diagnostics := make([]*ast.Diagnostic, 0, len(sourceFiles)) for _, sourceFile := range sourceFiles { - for _, diagnostic := range program.GetSyntacticDiagnostics(ctx, sourceFile) { - diagnosticMaps.addDiagnostic(diagnostic) - } - for _, diagnostic := range program.GetSemanticDiagnostics(ctx, sourceFile) { - diagnosticMaps.addDiagnostic(diagnostic) - } + diagnostics = append(diagnostics, program.GetSyntacticDiagnostics(ctx, sourceFile)...) + diagnostics = append(diagnostics, program.GetSemanticDiagnostics(ctx, sourceFile)...) + } + diagnostics = compiler.SortAndDeduplicateDiagnostics(diagnostics) + for _, diagnostic := range diagnostics { + diagnosticMaps.addDiagnostic(diagnostic) } return diagnosticMaps.getDiagnostics() } From 8ba82e87bb8a36bbf7ab380d627b84df448be559 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 24 Sep 2025 12:57:09 -0700 Subject: [PATCH 07/28] deno lib files + some fixes --- internal/api/server.go | 27 +++++++++++++++++++++++---- internal/compiler/fileloader.go | 8 ++++++++ internal/ls/api.go | 30 ++++++++++++++++++++++-------- internal/module/types.go | 20 ++++++++++---------- internal/tsoptions/enummaps.go | 18 ++++++++++++++++++ 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index 1ee18d2261..f1a718c3e1 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -189,6 +189,12 @@ func newResolverWrapper(inner module.ResolverInterface, server *Server) *resolve } } +type PackageJsonIfApplicable struct { + PackageDirectory string + DirectoryExists bool + Contents string +} + // GetPackageJsonScopeIfApplicable implements module.ResolverInterface. func (r *resolverWrapper) GetPackageJsonScopeIfApplicable(path string) *packagejson.InfoCacheEntry { if r.server.CallbackEnabled(CallbackGetPackageJsonScopeIfApplicable) { @@ -197,11 +203,23 @@ func (r *resolverWrapper) GetPackageJsonScopeIfApplicable(path string) *packagej panic(err) } if len(result) > 0 { - var res packagejson.InfoCacheEntry + var res PackageJsonIfApplicable if err := json.Unmarshal(result, &res); err != nil { panic(err) } - return &res + contents, err := packagejson.Parse([]byte(res.Contents)) + if err != nil { + panic(err) + } + return &packagejson.InfoCacheEntry{ + PackageDirectory: res.PackageDirectory, + DirectoryExists: res.DirectoryExists, + Contents: &packagejson.PackageJson{ + Fields: contents, + }, + } + } else { + return nil } } return r.inner.GetPackageJsonScopeIfApplicable(path) @@ -286,8 +304,9 @@ func NewServer(options *ServerOptions) *Server { fs: bundled.WrapFS(osvfs.FS()), defaultLibraryPath: options.DefaultLibraryPath, } - // logger := logging.NewLogger(options.Err) - logger := NoLogger{} + + logger := logging.NewLogger(options.Err) + // logger := NoLogger{} server.logger = logger server.api = NewAPI(&APIInit{ Logger: logger, diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index 0ba807719b..cd751295ee 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -607,12 +607,20 @@ func (p *fileLoader) createSyntheticImport(text string, file *ast.SourceFile) *a return externalHelpersModuleReference } +func isDenoLibFile(name string) bool { + return strings.HasPrefix(name, "lib.deno") +} + func (p *fileLoader) pathForLibFile(name string) *LibFile { if cached, ok := p.pathForLibFileCache.Load(name); ok { return cached } path := tspath.CombinePaths(p.defaultLibraryPath, name) + if isDenoLibFile(name) { + libFileName, _ := tsoptions.GetLibFileName(name) + path = tspath.CombinePaths("asset:///", libFileName) + } replaced := false if p.opts.Config.CompilerOptions().LibReplacement.IsTrue() && name != "lib.d.ts" { libraryName := getLibraryNameFromLibFileName(name) diff --git a/internal/ls/api.go b/internal/ls/api.go index 470a880970..b38633a9b2 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -45,13 +45,27 @@ func (l *LanguageService) GetTypeOfSymbol(ctx context.Context, symbol *ast.Symbo return checker.GetTypeOfSymbolAtLocation(symbol, nil) } + +type Position struct { + Line int64 `json:"line"` + Character int64 `json:"character"` +} + +func getPosition(file *ast.SourceFile, position int, ls *LanguageService) Position { + pos := ls.createLspPosition(position, file) + return Position{ + Line: int64(pos.Line), + Character: int64(pos.Character), + } +} + type DiagnosticId uint32 type Diagnostic struct { Id DiagnosticId `json:"id"` FileName string `json:"fileName"` - Pos int32 `json:"pos"` - End int32 `json:"end"` + Start Position `json:"start"` + End Position `json:"end"` Code int32 `json:"code"` Category string `json:"category"` Message string `json:"message"` @@ -67,7 +81,7 @@ type diagnosticMaps struct { diagnosticReverseMap map[*ast.Diagnostic]DiagnosticId } -func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic) DiagnosticId { +func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageService) DiagnosticId { if i, ok := d.diagnosticReverseMap[diagnostic]; ok { return i } @@ -76,8 +90,8 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic) DiagnosticId diag := &Diagnostic{ Id: id, FileName: diagnostic.File().FileName(), - Pos: int32(diagnostic.Loc().Pos()), - End: int32(diagnostic.Loc().End()), + Start: getPosition(diagnostic.File(), diagnostic.Loc().Pos(), ls), + End: getPosition(diagnostic.File(), diagnostic.Loc().End(), ls), Code: diagnostic.Code(), Category: diagnostic.Category().Name(), Message: diagnostic.Message(), @@ -88,11 +102,11 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic) DiagnosticId d.diagnosticReverseMap[diagnostic] = id for _, messageChain := range diagnostic.MessageChain() { - diag.MessageChain = append(diag.MessageChain, d.addDiagnostic(messageChain)) + diag.MessageChain = append(diag.MessageChain, d.addDiagnostic(messageChain, ls)) } for _, relatedInformation := range diagnostic.RelatedInformation() { - diag.RelatedInformation = append(diag.RelatedInformation, d.addDiagnostic(relatedInformation)) + diag.RelatedInformation = append(diag.RelatedInformation, d.addDiagnostic(relatedInformation, ls)) } d.diagnosticMapById[id] = diag @@ -125,7 +139,7 @@ func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { } diagnostics = compiler.SortAndDeduplicateDiagnostics(diagnostics) for _, diagnostic := range diagnostics { - diagnosticMaps.addDiagnostic(diagnostic) + diagnosticMaps.addDiagnostic(diagnostic, l) } return diagnosticMaps.getDiagnostics() } diff --git a/internal/module/types.go b/internal/module/types.go index 7999e022a3..ca72a3464e 100644 --- a/internal/module/types.go +++ b/internal/module/types.go @@ -60,20 +60,20 @@ func (p *PackageId) PackageName() string { } type LookupLocations struct { - FailedLookupLocations []string - AffectingLocations []string - ResolutionDiagnostics []*ast.Diagnostic + FailedLookupLocations []string `json:"failedLookupLocations"` + AffectingLocations []string `json:"affectingLocations"` + ResolutionDiagnostics []*ast.Diagnostic `json:"resolutionDiagnostics"` } type ResolvedModule struct { LookupLocations - ResolvedFileName string - OriginalPath string - Extension string - ResolvedUsingTsExtension bool - PackageId PackageId - IsExternalLibraryImport bool - AlternateResult string + ResolvedFileName string `json:"resolvedFileName"` + OriginalPath string `json:"originalPath"` + Extension string `json:"extension"` + ResolvedUsingTsExtension bool `json:"resolvedUsingTsExtension"` + PackageId PackageId `json:"packageId"` + IsExternalLibraryImport bool `json:"isExternalLibraryImport"` + AlternateResult string `json:"alternateResult"` } func (r *ResolvedModule) IsResolved() bool { diff --git a/internal/tsoptions/enummaps.go b/internal/tsoptions/enummaps.go index 89a9ba9bfd..798a8c62f1 100644 --- a/internal/tsoptions/enummaps.go +++ b/internal/tsoptions/enummaps.go @@ -110,6 +110,24 @@ var LibMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, an {Key: "esnext.float16", Value: "lib.esnext.float16.d.ts"}, {Key: "decorators", Value: "lib.decorators.d.ts"}, {Key: "decorators.legacy", Value: "lib.decorators.legacy.d.ts"}, + {Key: "deno.window", Value: "lib.deno.window.d.ts"}, + {Key: "deno.worker", Value: "lib.deno.worker.d.ts"}, + {Key: "deno.ns", Value: "lib.deno.ns.d.ts"}, + + {Key: "deno.console", Value: "lib.deno.console.d.ts"}, + {Key: "deno.url", Value: "lib.deno.url.d.ts"}, + {Key: "deno.web", Value: "lib.deno.web.d.ts"}, + {Key: "deno.fetch", Value: "lib.deno.fetch.d.ts"}, + {Key: "deno.websocket", Value: "lib.deno.websocket.d.ts"}, + {Key: "deno.webstorage", Value: "lib.deno.webstorage.d.ts"}, + {Key: "deno.canvas", Value: "lib.deno.canvas.d.ts"}, + {Key: "deno.crypto", Value: "lib.deno.crypto.d.ts"}, + {Key: "deno.broadcast_channel", Value: "lib.deno.broadcast_channel.d.ts"}, + {Key: "deno.net", Value: "lib.deno.net.d.ts"}, + {Key: "deno.cache", Value: "lib.deno.cache.d.ts"}, + {Key: "deno.webgpu", Value: "lib.deno.webgpu.d.ts"}, + {Key: "deno.shared_globals", Value: "lib.deno.shared_globals.d.ts"}, + {Key: "deno.unstable", Value: "lib.deno.unstable.d.ts"}, }) var ( From 8702c887decce7860c1db9a32e65fbddb7b76c1e Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 24 Sep 2025 18:04:52 -0700 Subject: [PATCH 08/28] return both line/char and byte pos --- cmd/tsgo/api.go | 3 +++ internal/api/server.go | 9 +++++++-- internal/ls/api.go | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/tsgo/api.go b/cmd/tsgo/api.go index 9438c39115..7493a2ed94 100644 --- a/cmd/tsgo/api.go +++ b/cmd/tsgo/api.go @@ -20,6 +20,8 @@ func runAPI(args []string) int { } defaultLibraryPath := bundled.LibPath() + + logEnabled := os.Getenv("TSGO_LOG_ENABLED") == "1" s := api.NewServer(&api.ServerOptions{ In: os.Stdin, @@ -27,6 +29,7 @@ func runAPI(args []string) int { Err: os.Stderr, Cwd: *cwd, DefaultLibraryPath: defaultLibraryPath, + LogEnabled: logEnabled, }) if err := s.Run(); err != nil && !errors.Is(err, io.EOF) { diff --git a/internal/api/server.go b/internal/api/server.go index f1a718c3e1..5e69594e79 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -77,6 +77,7 @@ type ServerOptions struct { Err io.Writer Cwd string DefaultLibraryPath string + LogEnabled bool } var _ vfs.FS = (*Server)(nil) @@ -305,8 +306,12 @@ func NewServer(options *ServerOptions) *Server { defaultLibraryPath: options.DefaultLibraryPath, } - logger := logging.NewLogger(options.Err) - // logger := NoLogger{} + var logger logging.Logger + if options.LogEnabled { + logger = logging.NewLogger(options.Err) + } else { + logger = NoLogger{} + } server.logger = logger server.api = NewAPI(&APIInit{ Logger: logger, diff --git a/internal/ls/api.go b/internal/ls/api.go index b38633a9b2..d198f12127 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -66,6 +66,8 @@ type Diagnostic struct { FileName string `json:"fileName"` Start Position `json:"start"` End Position `json:"end"` + StartPos int `json:"startPos"` + EndPos int `json:"endPos"` Code int32 `json:"code"` Category string `json:"category"` Message string `json:"message"` @@ -92,6 +94,8 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageS FileName: diagnostic.File().FileName(), Start: getPosition(diagnostic.File(), diagnostic.Loc().Pos(), ls), End: getPosition(diagnostic.File(), diagnostic.Loc().End(), ls), + StartPos: diagnostic.Loc().Pos(), + EndPos: diagnostic.Loc().End(), Code: diagnostic.Code(), Category: diagnostic.Category().Name(), Message: diagnostic.Message(), From 4b340a15e856e539e64ef996d4d1eb91b2f5b222 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 25 Sep 2025 10:47:37 -0700 Subject: [PATCH 09/28] implied node format on resolver --- internal/api/server.go | 23 +++++++++++++++++++++++ internal/compiler/fileloader.go | 2 +- internal/module/resolver.go | 7 +++++++ internal/module/types.go | 10 +++++----- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/internal/api/server.go b/internal/api/server.go index 5e69594e79..1eb0ba619d 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -69,6 +69,7 @@ const ( CallbackResolveTypeReferenceDirective CallbackGetPackageJsonScopeIfApplicable CallbackGetPackageScopeForPath + CallbackGetImpliedNodeFormatForFile ) type ServerOptions struct { @@ -290,6 +291,26 @@ func (r *resolverWrapper) ResolveTypeReferenceDirective(typeReferenceDirectiveNa return r.inner.ResolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, resolutionMode, redirectedReference) } +func (r *resolverWrapper) GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind { + if r.server.CallbackEnabled(CallbackGetImpliedNodeFormatForFile) { + result, err := r.server.call("getImpliedNodeFormatForFile", map[string]any{ + "fileName": path, + "packageJsonType": packageJsonType, + }) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res core.ModuleKind + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + return res + } + } + return r.inner.GetImpliedNodeFormatForFile(path, packageJsonType) +} + var _ module.ResolverInterface = (*resolverWrapper)(nil) func NewServer(options *ServerOptions) *Server { @@ -475,6 +496,8 @@ func (s *Server) enableCallback(callback string) error { s.enabledCallbacks |= CallbackGetPackageJsonScopeIfApplicable case "getPackageScopeForPath": s.enabledCallbacks |= CallbackGetPackageScopeForPath + case "getImpliedNodeFormatForFile": + s.enabledCallbacks |= CallbackGetImpliedNodeFormatForFile default: return fmt.Errorf("unknown callback: %s", callback) } diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index cd751295ee..8ee25cba99 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -408,7 +408,7 @@ func (p *fileLoader) loadSourceFileMetaData(fileName string) ast.SourceFileMetaD packageJsonType = value } } - impliedNodeFormat := ast.GetImpliedNodeFormatForFile(fileName, packageJsonType) + impliedNodeFormat := p.resolver.GetImpliedNodeFormatForFile(fileName, packageJsonType) return ast.SourceFileMetaData{ PackageJsonType: packageJsonType, PackageJsonDirectory: packageJsonDirectory, diff --git a/internal/module/resolver.go b/internal/module/resolver.go index 72cf42cf8d..ac38374851 100644 --- a/internal/module/resolver.go +++ b/internal/module/resolver.go @@ -147,6 +147,7 @@ type ResolverInterface interface { ResolveTypeReferenceDirective(typeReferenceDirectiveName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference ResolvedProjectReference) (*ResolvedTypeReferenceDirective, []string) GetPackageJsonScopeIfApplicable(path string) *packagejson.InfoCacheEntry GetPackageScopeForPath(directory string) *packagejson.InfoCacheEntry + GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind } type Resolver struct { @@ -158,6 +159,8 @@ type Resolver struct { // reportDiagnostic: DiagnosticReporter } +var _ ResolverInterface = (*Resolver)(nil) + func NewResolver( host ResolutionHost, options *core.CompilerOptions, @@ -274,6 +277,10 @@ func (r *Resolver) ResolveModuleName(moduleName string, containingFile string, r return r.tryResolveFromTypingsLocation(moduleName, containingDirectory, result, traceBuilder), traceBuilder.getTraces() } +func (r *Resolver) GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind { + return ast.GetImpliedNodeFormatForFile(path, packageJsonType) +} + func (r *Resolver) tryResolveFromTypingsLocation(moduleName string, containingDirectory string, originalResult *ResolvedModule, traceBuilder *tracer) *ResolvedModule { if r.typingsLocation == "" || tspath.IsExternalModuleNameRelative(moduleName) || diff --git a/internal/module/types.go b/internal/module/types.go index ca72a3464e..df3dd0e13f 100644 --- a/internal/module/types.go +++ b/internal/module/types.go @@ -86,11 +86,11 @@ func (r *ResolvedModule) GetLookupLocations() *LookupLocations { type ResolvedTypeReferenceDirective struct { LookupLocations - Primary bool - ResolvedFileName string - OriginalPath string - PackageId PackageId - IsExternalLibraryImport bool + Primary bool `json:"primary"` + ResolvedFileName string `json:"resolvedFileName"` + OriginalPath string `json:"originalPath"` + PackageId PackageId `json:"packageId"` + IsExternalLibraryImport bool `json:"isExternalLibraryImport"` } func (r *ResolvedTypeReferenceDirective) IsResolved() bool { From 4a3dc8b50fb1b211410f95a56d1de4153d53cd2b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 25 Sep 2025 15:51:50 -0700 Subject: [PATCH 10/28] add isNodeSourceFile hook --- internal/api/server.go | 5 +++++ internal/checker/checker.go | 2 ++ internal/compiler/emitHost.go | 5 +++++ internal/compiler/host.go | 5 +++++ internal/compiler/program.go | 4 ++++ internal/execute/build/compilerHost.go | 4 ++++ internal/execute/build/host.go | 5 +++++ internal/modulespecifiers/types.go | 1 + internal/project/compilerhost.go | 5 +++++ internal/transformers/tstransforms/importelision_test.go | 4 ++++ 10 files changed, 40 insertions(+) diff --git a/internal/api/server.go b/internal/api/server.go index 1eb0ba619d..327eb005f8 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -171,6 +171,11 @@ func (h *hostWrapper) SessionOptions() *project.SessionOptions { return h.inner.SessionOptions() } +// IsNodeSourceFile implements project.ProjectHost. +func (h *hostWrapper) IsNodeSourceFile(path tspath.Path) bool { + return h.inner.IsNodeSourceFile(path) +} + func newProjectHostWrapper(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree, server *Server) *hostWrapper { inner := project.NewProjectHost(currentDirectory, proj, builder, logger) return &hostWrapper{ diff --git a/internal/checker/checker.go b/internal/checker/checker.go index ba18a4afb0..91b54c4645 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -547,6 +547,8 @@ type Program interface { type Host interface { modulespecifiers.ModuleSpecifierGenerationHost + + IsNodeSourceFile(path tspath.Path) bool } // Checker diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index fcc0406342..55682d481e 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -34,6 +34,11 @@ type emitHost struct { emitResolver printer.EmitResolver } +// IsNodeSourceFile implements EmitHost. +func (host *emitHost) IsNodeSourceFile(path tspath.Path) bool { + return host.program.IsNodeSourceFile(path) +} + func newEmitHost(ctx context.Context, program *Program, file *ast.SourceFile) (*emitHost, func()) { checker, done := program.GetTypeCheckerForFile(ctx, file) return &emitHost{ diff --git a/internal/compiler/host.go b/internal/compiler/host.go index 479301c3e1..03119db8f3 100644 --- a/internal/compiler/host.go +++ b/internal/compiler/host.go @@ -19,6 +19,7 @@ type CompilerHost interface { GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine MakeResolver(host module.ResolutionHost, options *core.CompilerOptions, typingsLocation string, projectName string) module.ResolverInterface + IsNodeSourceFile(path tspath.Path) bool } var _ CompilerHost = (*compilerHost)(nil) @@ -76,6 +77,10 @@ func (h *compilerHost) Trace(msg string) { h.trace(msg) } +func (h *compilerHost) IsNodeSourceFile(path tspath.Path) bool { + return false +} + func (h *compilerHost) GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile { text, ok := h.FS().ReadFile(opts.FileName) if !ok { diff --git a/internal/compiler/program.go b/internal/compiler/program.go index d571f10257..68cc2651c5 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -159,6 +159,10 @@ func (p *Program) UsesUriStyleNodeCoreModules() bool { return p.usesUriStyleNodeCoreModules.IsTrue() } +func (p *Program) IsNodeSourceFile(path tspath.Path) bool { + return p.Host().IsNodeSourceFile(path) +} + var _ checker.Program = (*Program)(nil) /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ diff --git a/internal/execute/build/compilerHost.go b/internal/execute/build/compilerHost.go index e59db25716..3e12c7ac0a 100644 --- a/internal/execute/build/compilerHost.go +++ b/internal/execute/build/compilerHost.go @@ -29,6 +29,10 @@ func (h *compilerHost) GetCurrentDirectory() string { return h.host.GetCurrentDirectory() } +func (h *compilerHost) IsNodeSourceFile(path tspath.Path) bool { + return h.host.IsNodeSourceFile(path) +} + func (h *compilerHost) Trace(msg string) { h.trace(msg) } diff --git a/internal/execute/build/host.go b/internal/execute/build/host.go index ab694c47e3..28951e6915 100644 --- a/internal/execute/build/host.go +++ b/internal/execute/build/host.go @@ -43,6 +43,11 @@ func (h *host) MakeResolver(host module.ResolutionHost, options *core.CompilerOp return h.host.MakeResolver(host, options, typingsLocation, projectName) } +// IsNodeSourceFile implements compiler.CompilerHost. +func (h *host) IsNodeSourceFile(path tspath.Path) bool { + return h.host.IsNodeSourceFile(path) +} + func (h *host) DefaultLibraryPath() string { return h.host.DefaultLibraryPath() } diff --git a/internal/modulespecifiers/types.go b/internal/modulespecifiers/types.go index eb9881f656..f750be19c2 100644 --- a/internal/modulespecifiers/types.go +++ b/internal/modulespecifiers/types.go @@ -63,6 +63,7 @@ type ModuleSpecifierGenerationHost interface { GetDefaultResolutionModeForFile(file ast.HasFileName) core.ResolutionMode GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule GetModeForUsageLocation(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) core.ResolutionMode + IsNodeSourceFile(path tspath.Path) bool } type ImportModuleSpecifierPreference string diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index 2cb753951f..80d3704e29 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -45,6 +45,11 @@ type compilerHost struct { logger *logging.LogTree } +// IsNodeSourceFile implements compiler.CompilerHost. +func (c *compilerHost) IsNodeSourceFile(path tspath.Path) bool { + return false +} + type builderFileSource struct { seenFiles *collections.SyncSet[tspath.Path] snapshotFSBuilder *snapshotFSBuilder diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go index 3dc5c227cd..7e48c24501 100644 --- a/internal/transformers/tstransforms/importelision_test.go +++ b/internal/transformers/tstransforms/importelision_test.go @@ -165,6 +165,10 @@ func (p *fakeProgram) IsSourceFileDefaultLibrary(path tspath.Path) bool { return false } +func (p *fakeProgram) IsNodeSourceFile(path tspath.Path) bool { + return false +} + func TestImportElision(t *testing.T) { t.Parallel() data := []struct { From f1a32c5a3fdd8ed1246a86616b31e77d07335281 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 25 Sep 2025 16:45:54 -0700 Subject: [PATCH 11/28] wip --- internal/checker/checker.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 91b54c4645..5a0a1801e3 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -1324,6 +1324,7 @@ func (c *Checker) initializeChecker() { } } + func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { moduleNode := moduleName.Parent moduleAugmentation := moduleNode.AsModuleDeclaration() @@ -13455,6 +13456,25 @@ func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTa return combined } + +// func (c *Checker) mergeGlobalSymbolTable(node *ast.Node, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { +// // sourceFile := ast.GetSourceFileOfNode(node) +// // // isNodeFile := c.program.IsNodeSourceFile(sourceFile.Path()) +// // for id, sourceSymbol := range source { +// // target := c.globals +// // targetSymbol := target[id] +// // // var merged *ast.Symbol +// // if targetSymbol != nil { +// // merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) +// // } else { +// // merged = c.getMergedSymbol(sourceSymbol) +// // } +// // } + + + +// } + func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { for id, sourceSymbol := range source { targetSymbol := target[id] From 7ee6616b9807c179fe1eb9f1fc011e4171ffdeec Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 25 Sep 2025 17:05:27 -0700 Subject: [PATCH 12/28] massive symbol table refactor --- internal/ast/symbol.go | 106 ++++- internal/ast/utilities.go | 2 +- internal/binder/binder.go | 30 +- internal/binder/nameresolver.go | 6 +- internal/checker/checker.go | 406 ++++++++---------- internal/checker/emitresolver.go | 7 +- internal/checker/inference.go | 8 +- internal/checker/jsx.go | 22 +- internal/checker/nodebuilderimpl.go | 12 +- internal/checker/nodebuilderscopes.go | 10 +- internal/checker/services.go | 38 +- internal/checker/symbolaccessibility.go | 31 +- internal/checker/utilities.go | 12 +- .../incremental/affectedfileshandler.go | 2 +- internal/ls/completions.go | 6 +- internal/ls/definition.go | 2 +- internal/ls/findallreferences.go | 2 +- internal/ls/hover.go | 2 +- internal/modulespecifiers/specifiers.go | 2 +- internal/printer/namegenerator.go | 2 +- 20 files changed, 396 insertions(+), 312 deletions(-) diff --git a/internal/ast/symbol.go b/internal/ast/symbol.go index 926d9bd53a..a16789afe5 100644 --- a/internal/ast/symbol.go +++ b/internal/ast/symbol.go @@ -1,6 +1,8 @@ package ast import ( + "iter" + "maps" "sync/atomic" "github.com/microsoft/typescript-go/internal/collections" @@ -25,7 +27,109 @@ type Symbol struct { // SymbolTable -type SymbolTable map[string]*Symbol +// type SymbolTable map[string]*Symbol + +type SymbolTable interface { + Get(name string) *Symbol + Get2(name string) (*Symbol, bool) + Set(name string, symbol *Symbol) + Delete(name string) + Keys() iter.Seq[string] + Values() iter.Seq[*Symbol] + Each(func(name string, symbol *Symbol) ) + Iter() iter.Seq2[string, *Symbol] + Len() int + Clone() SymbolTable + Find(predicate func(*Symbol) bool) *Symbol + +} + +type SymbolMap struct { + m map[string]*Symbol +} + +func (m *SymbolMap) Find(predicate func(*Symbol) bool) *Symbol { + for _, symbol := range m.m { + if predicate(symbol) { + return symbol + } + } + return nil +} + +func (m *SymbolMap) Clone() SymbolTable { + return &SymbolMap{m: maps.Clone(m.m)} +} + +func (m *SymbolMap) Len() int { + return len(m.m) +} + +func (m *SymbolMap) Iter() iter.Seq2[string, *Symbol] { + return func(yield func (string, *Symbol) bool) { + for name, symbol := range m.m { + if !yield(name, symbol) { + return + } + } + } +} + +func (m *SymbolMap) Get(name string) *Symbol { + return m.m[name] +} + +func (m *SymbolMap) Get2(name string) (*Symbol, bool) { + symbol, ok := m.m[name] + return symbol, ok +} + +func (m *SymbolMap) Set(name string, symbol *Symbol) { + m.m[name] = symbol +} + +func (m *SymbolMap) Delete(name string) { + delete(m.m, name) +} + +func (m *SymbolMap) Keys() iter.Seq[string] { + return func(yield func (string) bool) { + for name := range m.m { + if !yield(name) { + return + } + } + } +} + +func (m *SymbolMap) Values() iter.Seq[*Symbol] { + return func(yield func (*Symbol) bool) { + for _, symbol := range m.m { + if !yield(symbol) { + return + } + } + } +} + +func (m *SymbolMap) Each(fn func(name string, symbol *Symbol) ) { + for name, symbol := range m.m { + fn(name, symbol) + } +} + +func NewSymbolTable() SymbolTable { + return &SymbolMap{m: make(map[string]*Symbol)} +} + +func NewSymbolTableWithCapacity(capacity int) SymbolTable { + return &SymbolMap{m: make(map[string]*Symbol, capacity)} +} + +func NewSymbolTableFromMap(m map[string]*Symbol) SymbolTable { + return &SymbolMap{m: m} +} + const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index b5ae76d503..99e33b65ad 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -45,7 +45,7 @@ func GetSymbolId(symbol *Symbol) SymbolId { func GetSymbolTable(data *SymbolTable) SymbolTable { if *data == nil { - *data = make(SymbolTable) + *data = NewSymbolTable() } return *data } diff --git a/internal/binder/binder.go b/internal/binder/binder.go index b56d369d00..e21aca2daf 100644 --- a/internal/binder/binder.go +++ b/internal/binder/binder.go @@ -189,13 +189,13 @@ 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 includes&ast.SymbolFlagsClassifiable != 0 { b.classifiableNames.Add(name) } if symbol == nil { symbol = b.newSymbol(ast.SymbolFlagsNone, name) - symbolTable[name] = symbol + symbolTable.Set(name, symbol) if isReplaceableByMethod { symbol.Flags |= ast.SymbolFlagsReplaceableByMethod } @@ -207,7 +207,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. @@ -950,11 +950,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)[prototypeSymbol.Name] + symbolExport := ast.GetExports(symbol).Get(prototypeSymbol.Name) if symbolExport != nil { b.errorOnNode(symbolExport.Declarations[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol)) } - ast.GetExports(symbol)[prototypeSymbol.Name] = prototypeSymbol + ast.GetExports(symbol).Set(prototypeSymbol.Name, prototypeSymbol) prototypeSymbol.Parent = symbol } @@ -980,8 +980,8 @@ 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.Members = make(ast.SymbolTable) - typeLiteralSymbol.Members[symbol.Name] = symbol + typeLiteralSymbol.Members = ast.NewSymbolTable() + typeLiteralSymbol.Members.Set(symbol.Name, symbol) } func addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbol) { @@ -1220,29 +1220,29 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { if ast.IsPropertyAccessExpression(node) && node.AsPropertyAccessExpression().Expression.Kind == ast.KindThisKeyword || ast.IsElementAccessExpression(node) && node.AsElementAccessExpression().Expression.Kind == ast.KindThisKeyword { if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { - if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbolTable[name.Text()] - } + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + 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()] - } + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + return symbol.Exports.Get(name.Text()) + } } return nil } 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 } diff --git a/internal/binder/nameresolver.go b/internal/binder/nameresolver.go index dbac222247..e6b9965715 100644 --- a/internal/binder/nameresolver.go +++ b/internal/binder/nameresolver.go @@ -97,7 +97,7 @@ loop: 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 { @@ -116,7 +116,7 @@ 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 } @@ -426,7 +426,7 @@ func (r *NameResolver) lookup(symbols ast.SymbolTable, name string, meaning ast. } // 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/internal/checker/checker.go b/internal/checker/checker.go index 5a0a1801e3..41fe913ad3 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -888,7 +888,7 @@ func NewChecker(program Program) *Checker { 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) @@ -921,7 +921,7 @@ func NewChecker(program Program) *Checker { c.errorTypes = make(map[string]*Type) c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.globalThisSymbol.Exports = c.globals - 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[string]*Type) @@ -1088,7 +1088,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 @@ -1264,9 +1264,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.Symbol.GlobalExports { - if _, ok := c.globals[name]; !ok { - c.globals[name] = symbol + for name, symbol := range file.Symbol.GlobalExports.Iter() { + if _, ok := c.globals.Get2(name); !ok { + c.globals.Set(name, symbol) } } } @@ -1360,14 +1360,14 @@ 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.patternAmbientModuleAugmentations).Set(moduleName.Text(), merged) } 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.Iter() { + if resolvedExports.Get(key) != nil && mainModule.Exports.Get(key) == nil { + c.mergeSymbol(resolvedExports.Get(key), value, false /*unidirectional*/) } } } @@ -1382,7 +1382,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) { @@ -1390,7 +1390,7 @@ func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { } } } else { - c.globals[name] = c.undefinedSymbol + c.globals.Set(name, c.undefinedSymbol) } } @@ -1670,10 +1670,10 @@ func (c *Checker) getSuggestionForSymbolNameLookup(symbols ast.SymbolTable, name if symbol != nil { return symbol } - allSymbols := slices.Collect(maps.Values(symbols)) + allSymbols := slices.Collect(symbols.Values()) if meaning&ast.SymbolFlagsGlobalLookup != 0 { for _, s := range []string{"stringString", "numberNumber", "booleanBoolean", "objectObject", "bigintBigInt", "symbolSymbol"} { - if _, ok := symbols[s[len(s)/2:]]; ok { + if _, ok := symbols.Get2(s[len(s)/2:]); ok { allSymbols = append(allSymbols, c.newSymbol(ast.SymbolFlagsTypeAlias, s[:len(s)/2])) } } @@ -2030,7 +2030,7 @@ func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, include ast. if name == "" { name = symbol.Name } - resolved = c.resolveSymbol(c.getExportsOfModule(links.typeOnlyDeclaration.Symbol().Parent)[name]) + resolved = c.resolveSymbol(c.getExportsOfModule(links.typeOnlyDeclaration.Symbol().Parent).Get(name)) } else { resolved = c.resolveAlias(links.typeOnlyDeclaration.Symbol()) } @@ -2065,7 +2065,7 @@ 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]) + symbol := c.getMergedSymbol(symbols.Get(name)) if symbol != nil { if symbol.Flags&meaning != 0 { return symbol @@ -3590,7 +3590,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) } } @@ -4043,11 +4043,9 @@ 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().Iter() { 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) - } - } + } } } } } @@ -4793,11 +4791,9 @@ func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.No return true } seen := make(map[string]InheritanceInfo) - for id, p := range c.resolveDeclaredMembers(t).declaredMembers { - if c.isNamedMember(p, id) { + for id, p := range c.resolveDeclaredMembers(t).declaredMembers.Iter() { if c.isNamedMember(p, id) { seen[p.Name] = InheritanceInfo{prop: p, containingType: t} - } - } + } } identical := true for _, base := range baseTypes { properties := c.getPropertiesOfType(c.getTypeWithThisArgument(base, t.AsInterfaceType().thisType, false)) @@ -5197,11 +5193,11 @@ func (c *Checker) getTypeFromImportAttributes(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 _, attr := range node.AsImportAttributes().Attributes.Nodes { member := c.newSymbol(ast.SymbolFlagsProperty, attr.Name().Text()) c.valueSymbolLinks.Get(member).resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(attr.AsImportAttribute().Value)) - members[member.Name] = member + members.Set(member.Name, member) } t := c.newAnonymousType(symbol, members, nil, nil, nil) t.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsNonInferrableType @@ -5418,7 +5414,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) if exportEqualsSymbol != nil && c.hasExportedMembers(moduleSymbol) { declaration := core.OrElse(c.getDeclarationOfAliasSymbol(exportEqualsSymbol), exportEqualsSymbol.ValueDeclaration) if declaration != nil && !isTopLevelInExternalModuleAugmentation(declaration) { @@ -5426,41 +5422,37 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } } // Checks for export * conflicts - for id, symbol := range c.getExportsOfModule(moduleSymbol) { - if id == ast.InternalSymbolNameExportStar { + for id, symbol := range c.getExportsOfModule(moduleSymbol).Iter() { if id == ast.InternalSymbolNameExportStar { continue } - // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. - // (TS Exceptions: namespaces, function overloads, enums, and interfaces) - if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 { +// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. +// (TS Exceptions: namespaces, function overloads, enums, and interfaces) +if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 { continue } - exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool { +exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool { return isNotOverload(d) && !ast.IsAccessor(d) && !ast.IsInterfaceDeclaration(d) }) - if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 { +if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 { // it is legal to merge type alias with other values // so count should be either 1 (just type alias) or 2 (type alias + merged value) continue } - if exportedDeclarationsCount > 1 { +if exportedDeclarationsCount > 1 { for _, declaration := range symbol.Declarations { if isNotOverload(declaration) { c.error(declaration, diagnostics.Cannot_redeclare_exported_variable_0, id) } } - } - } + } } links.exportsChecked = true } } func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool { - for id := range moduleSymbol.Exports { - if id != ast.InternalSymbolNameExportEquals { + for id := range moduleSymbol.Exports.Iter() { if id != ast.InternalSymbolNameExportEquals { return true - } - } + } } return false } @@ -6238,8 +6230,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() @@ -6758,13 +6750,12 @@ 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 - if local.Flags&ast.SymbolFlagsTypeParameter != 0 && (local.Flags&ast.SymbolFlagsVariable == 0 || referenceKinds&ast.SymbolFlagsVariable != 0) || + for _, local := range node.Locals().Iter() { referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds +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) { continue } - for _, declaration := range local.Declarations { +for _, declaration := range local.Declarations { switch { case ast.IsVariableDeclaration(declaration) || ast.IsParameter(declaration) || ast.IsBindingElement(declaration): variableParents.Add(ast.GetRootDeclaration(declaration).Parent) @@ -6781,8 +6772,7 @@ func (c *Checker) checkUnusedLocalsAndParameters(node *ast.Node) { c.reportUnusedLocal(declaration, ast.SymbolName(local)) } } - } - } + } } for declaration := range variableParents.Keys() { if ast.IsVariableDeclarationList(declaration) { c.reportUnusedVariables(declaration) @@ -10822,7 +10812,7 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l } if indexInfo == nil { 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 { @@ -10956,11 +10946,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 } @@ -12565,9 +12555,9 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type c.checkGrammarObjectLiteralExpression(node.AsObjectLiteralExpression(), inDestructuringPattern) var allPropertiesTable ast.SymbolTable if c.strictNullChecks { - allPropertiesTable = make(ast.SymbolTable) + allPropertiesTable = ast.NewSymbolTable() } - propertiesTable := make(ast.SymbolTable) + propertiesTable := ast.NewSymbolTable() var propertiesArray []*ast.Symbol spread := c.emptyObjectType c.pushCachedContextualType(node) @@ -12620,7 +12610,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type return result } // expando object literals have empty properties but filled exports -- skip straight to type creation - if len(node.AsObjectLiteralExpression().Properties.Nodes) == 0 && node.Symbol() != nil && len(node.Symbol().Exports) > 0 { + if len(node.AsObjectLiteralExpression().Properties.Nodes) == 0 && node.Symbol() != nil && node.Symbol().Exports.Len() > 0 { propertiesTable = node.Symbol().Exports return createObjectLiteralType() } @@ -12676,7 +12666,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type 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) @@ -12691,7 +12681,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type if len(propertiesArray) > 0 { spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) propertiesArray = nil - propertiesTable = make(ast.SymbolTable) + propertiesTable = ast.NewSymbolTable() hasComputedStringProperty = false hasComputedNumberProperty = false hasComputedSymbolProperty = false @@ -12735,7 +12725,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type } } } else { - propertiesTable[member.Name] = member + propertiesTable.Set(member.Name, member) } propertiesArray = append(propertiesArray, member) } @@ -12747,7 +12737,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type if len(propertiesArray) > 0 { spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) propertiesArray = nil - propertiesTable = make(ast.SymbolTable) + propertiesTable = ast.NewSymbolTable() hasComputedStringProperty = false hasComputedNumberProperty = false } @@ -12765,7 +12755,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *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 { - 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)) } @@ -12830,7 +12820,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj } return c.getIntersectionType([]*Type{left, right}) } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() var skippedPrivateMembers collections.Set[string] var indexInfos []*IndexInfo if left == c.emptyObjectType { @@ -12842,7 +12832,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)) } } @@ -12850,8 +12840,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) @@ -12874,10 +12864,10 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj c.spreadLinks.Get(result).rightSpread = rightProp result.Declarations = declarations links.nameType = c.valueSymbolLinks.Get(leftProp).nameType - members[leftProp.Name] = result + 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 { @@ -12945,7 +12935,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 := ast.NewSymbolTable() for _, prop := range c.getPropertiesOfType(firstType) { if getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { // do nothing, skip privates @@ -12962,7 +12952,7 @@ func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool result.Declarations = prop.Declarations links.nameType = c.valueSymbolLinks.Get(prop).nameType c.mappedSymbolLinks.Get(result).syntheticOrigin = prop - members[prop.Name] = result + members.Set(prop.Name, result) } } spread := c.newAnonymousType(firstType.symbol, members, nil, nil, c.getIndexInfosOfType(firstType)) @@ -13444,13 +13434,13 @@ 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 @@ -13476,15 +13466,14 @@ func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTa // } func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { - for id, sourceSymbol := range source { - targetSymbol := target[id] - var merged *ast.Symbol - if targetSymbol != nil { + for id, sourceSymbol := range source.Iter() { targetSymbol := target.Get(id) +var merged *ast.Symbol +if targetSymbol != nil { merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) } else { merged = c.getMergedSymbol(sourceSymbol) } - if mergedParent != nil && targetSymbol != nil { +if mergedParent != nil && targetSymbol != nil { // If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge // of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent: // @@ -13504,8 +13493,7 @@ func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTabl merged.Parent = mergedParent } } - target[id] = merged - } +target.Set(id, merged) } } /** @@ -13738,8 +13726,8 @@ func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol { result.Declarations = symbol.Declarations[0:len(symbol.Declarations):len(symbol.Declarations)] result.Parent = symbol.Parent result.ValueDeclaration = symbol.ValueDeclaration - result.Members = maps.Clone(symbol.Members) - result.Exports = maps.Clone(symbol.Exports) + result.Members = symbol.Members.Clone() + result.Exports = symbol.Exports.Clone() c.recordMergedSymbol(result, symbol) return result } @@ -13922,7 +13910,7 @@ func (c *Checker) getTargetOfModuleDefault(moduleSymbol *ast.Symbol, node *ast.N if exportDefaultSymbol == nil && !hasSyntheticDefault && !hasDefaultOnly { if hasExportAssignmentSymbol(moduleSymbol) && !c.allowSyntheticDefaultImports { compilerOptionName := core.IfElse(c.moduleKind >= core.ModuleKindES2015, "allowSyntheticDefaultImports", "esModuleInterop") - exportEqualsSymbol := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] + exportEqualsSymbol := moduleSymbol.Exports.Get(ast.InternalSymbolNameExportEquals) exportAssignment := exportEqualsSymbol.ValueDeclaration err := c.error(node.Name(), diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, c.symbolToString(moduleSymbol), compilerOptionName) if exportAssignment != nil { @@ -13953,13 +13941,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 { @@ -13967,7 +13955,7 @@ func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Nod return false } resolvedExternalModuleName := c.resolveExternalModuleName(decl, decl.AsExportDeclaration().ModuleSpecifier, false /*ignoreErrors*/) - 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)) @@ -13977,12 +13965,12 @@ 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, exportSymbol, resolved, false /*overwriteEmpty*/, nil, "") @@ -14060,7 +14048,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) @@ -14138,14 +14126,14 @@ func (c *Checker) combineValueAndTypeSymbols(valueSymbol *ast.Symbol, typeSymbol result.Parent = typeSymbol.Parent } result.ValueDeclaration = valueSymbol.ValueDeclaration - result.Members = maps.Clone(typeSymbol.Members) - result.Exports = maps.Clone(valueSymbol.Exports) + result.Members = typeSymbol.Members.Clone() + result.Exports = valueSymbol.Exports.Clone() 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, exportSymbol, resolved /*overwriteEmpty*/, false, exportStarDeclaration, nameText) @@ -14241,7 +14229,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(node, name, declarationName, moduleSymbol, moduleName) @@ -14252,18 +14240,18 @@ func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymb func (c *Checker) reportNonExportedMember(node *ast.Node, 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(node, 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 { + exportedSymbol := exports.Find(func(symbol *ast.Symbol) bool { return c.getSymbolIfSameReference(symbol, localSymbol) != nil }) var diagnostic *ast.Diagnostic @@ -14444,7 +14432,7 @@ func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration *ast.N func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnlyWorker(aliasDeclarationLinks *AliasSymbolLinks, target *ast.Symbol, overwriteEmpty bool) bool { if target != nil && (!aliasDeclarationLinks.typeOnlyDeclarationResolved || overwriteEmpty && aliasDeclarationLinks.typeOnlyDeclaration == nil) { - exportSymbol := core.OrElse(target.Exports[ast.InternalSymbolNameExportEquals], target) + exportSymbol := core.OrElse(target.Exports.Get(ast.InternalSymbolNameExportEquals), target) aliasDeclarationLinks.typeOnlyDeclarationResolved = true if typeOnly := core.Find(exportSymbol.Declarations, ast.IsTypeOnlyImportOrExportDeclaration); typeOnly != nil { aliasDeclarationLinks.typeOnlyDeclaration = typeOnly @@ -14674,7 +14662,7 @@ func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference stri if len(c.patternAmbientModules) != 0 { pattern := core.FindBestPatternMatch(c.patternAmbientModules, func(v *ast.PatternAmbientModule) core.Pattern { return v.Pattern }, moduleReference) if pattern != nil { - augmentation := c.patternAmbientModuleAugmentations[moduleReference] + augmentation := c.patternAmbientModuleAugmentations.Get(moduleReference) if augmentation != nil { return c.getMergedSymbol(augmentation) } @@ -14864,18 +14852,16 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool func (c *Checker) GetAmbientModules() []*ast.Symbol { c.ambientModulesOnce.Do(func() { - for sym, global := range c.globals { - if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { + for sym, global := range c.globals.Iter() { if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { c.ambientModules = append(c.ambientModules, global) - } - } + } } }) return c.ambientModules } 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) } @@ -15005,12 +14991,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.Parent = originalSymbol c.valueSymbolLinks.Get(newSymbol).nameType = c.getStringLiteralType("default") c.aliasSymbolLinks.Get(newSymbol).aliasTarget = c.resolveSymbol(symbol) - memberTable[ast.InternalSymbolNameDefault] = newSymbol + memberTable.Set(ast.InternalSymbolNameDefault, newSymbol) return c.newAnonymousType(anonymousSymbol, memberTable, nil, nil, nil) } @@ -15018,8 +15004,8 @@ func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, re result := c.newSymbol(symbol.Flags, symbol.Name) result.Declarations = slices.Clone(symbol.Declarations) result.ValueDeclaration = symbol.ValueDeclaration - result.Members = maps.Clone(symbol.Members) - result.Exports = maps.Clone(symbol.Exports) + result.Members = symbol.Members.Clone() + result.Exports = symbol.Exports.Clone() result.Parent = symbol.Parent links := c.exportTypeLinks.Get(result) links.target = symbol @@ -15190,7 +15176,7 @@ func (c *Checker) tryGetQualifiedNameAsValue(node *ast.Node) *ast.Symbol { } func (c *Checker) getSuggestedSymbolForNonexistentModule(name *ast.Node, targetModule *ast.Symbol) *ast.Symbol { - exports := slices.Collect(maps.Values(c.getExportsOfModule(targetModule))) + exports := slices.Collect(c.getExportsOfModule(targetModule).Values()) c.sortSymbols(exports) return c.getSpellingSuggestionForName(name.Text(), exports, ast.SymbolFlagsModuleMember) } @@ -15232,12 +15218,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 */) } @@ -15248,7 +15234,7 @@ func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolu for member := range symbol.AssignmentDeclarationMembers.Keys() { if c.hasLateBindableName(member) { if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) + lateSymbols = ast.NewSymbolTable() } c.lateBindMember(symbol, earlySymbols, lateSymbols, member) } @@ -15308,13 +15294,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. @@ -15350,16 +15336,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.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, @@ -15417,7 +15403,7 @@ 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)) + nonTypeOnlyNames := collections.NewSetWithSizeHint[string](moduleSymbol.Exports.Len()) // 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 @@ -15426,19 +15412,17 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as // Add non-type-only names 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) - } + for name := range symbol.Exports.Iter() { nonTypeOnlyNames.Add(name) } } if symbol == nil || symbol.Exports == nil || slices.Contains(visitedSymbols, symbol) { return nil } visitedSymbols = append(visitedSymbols, symbol) - symbols := maps.Clone(symbol.Exports) + symbols := symbol.Exports.Clone() // All export * declarations are collected in an __export symbol by the binder - exportStars := symbol.Exports[ast.InternalSymbolNameExportStar] + exportStars := symbol.Exports.Get(ast.InternalSymbolNameExportStar) if exportStars != nil { - nestedSymbols := make(ast.SymbolTable) + nestedSymbols := ast.NewSymbolTable() lookupTable := make(ExportCollisionTable) for _, node := range exportStars.Declarations { resolvedModule := c.resolveExternalModuleName(node, node.AsExportDeclaration().ModuleSpecifier, false /*ignoreErrors*/) @@ -15447,7 +15431,7 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as } 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 { + if id == ast.InternalSymbolNameExportEquals || len(s.exportsWithDuplicate) == 0 || symbols.Get(id) != nil { continue } for _, node := range s.exportsWithDuplicate { @@ -15460,9 +15444,7 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as if typeOnlyExportStarMap == nil { typeOnlyExportStarMap = make(map[string]*ast.Node) } - for name := range symbols { - typeOnlyExportStarMap[name] = exportStar - } + for name := range symbols.Iter() { typeOnlyExportStarMap[name] = exportStar } } return symbols } @@ -15470,7 +15452,7 @@ 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() } for name := range nonTypeOnlyNames.Keys() { delete(typeOnlyExportStarMap, name) @@ -15483,13 +15465,12 @@ 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 { - if id == ast.InternalSymbolNameDefault { + for id, sourceSymbol := range source.Iter() { if id == ast.InternalSymbolNameDefault { continue } - targetSymbol := target[id] - if targetSymbol == nil { - target[id] = sourceSymbol +targetSymbol := target.Get(id) +if targetSymbol == nil { + target.Set(id, sourceSymbol) if lookupTable != nil && exportNode != nil { lookupTable[id] = &ExportCollision{ specifierText: scanner.GetTextOfNode(exportNode.AsExportDeclaration().ModuleSpecifier), @@ -15498,8 +15479,7 @@ func (c *Checker) extendExportSymbols(target ast.SymbolTable, source ast.SymbolT } else if lookupTable != nil && exportNode != nil && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { s := lookupTable[id] s.exportsWithDuplicate = append(s.exportsWithDuplicate, exportNode) - } - } + } } } func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) { @@ -15626,7 +15606,7 @@ func (c *Checker) getSymbolFlagsEx(symbol *ast.Symbol, excludeTypeOnlyMeanings b var seenSymbols collections.Set[*ast.Symbol] for symbol.Flags&ast.SymbolFlagsAlias != 0 { target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol)) - if !typeOnlyDeclarationIsExportStar && target == typeOnlyResolution || typeOnlyExportStarTargets[target.Name] == target { + if !typeOnlyDeclarationIsExportStar && target == typeOnlyResolution || typeOnlyExportStarTargets.Get(target.Name) == target { break } if target == c.unknownSymbol { @@ -15840,8 +15820,8 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo } if symbol.Flags&ast.SymbolFlagsModuleExports != 0 && symbol.ValueDeclaration != nil { fileSymbol := c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/) - members := make(ast.SymbolTable, 1) - members["exports"] = fileSymbol + members := ast.NewSymbolTableWithCapacity(1) + members.Set("exports", fileSymbol) return c.newAnonymousType(symbol, members, nil, nil, nil) } debug.AssertIsDefined(symbol.ValueDeclaration) @@ -16085,14 +16065,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*/, false /*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 @@ -17082,9 +17062,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 @@ -17175,7 +17155,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.AsBindingPattern().Elements.Nodes { @@ -17194,7 +17174,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 { @@ -17570,14 +17550,14 @@ func (c *Checker) getWidenedTypeWithContext(t *Type, context *WideningContext) * } func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContext) *Type { - 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.Get2(prop.Name); !ok { + members.Set(prop.Name, c.getUndefinedProperty(prop)) } } } @@ -18043,7 +18023,7 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr switch { case t.flags&TypeFlagsObject != 0: resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.members[name] + symbol := resolved.members.Get(name) 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 @@ -18269,7 +18249,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters baseTypes := c.getBaseTypes(source) if len(baseTypes) != 0 { if !instantiated { - members = maps.Clone(members) + members = members.Clone() } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) thisArgument := core.LastOrNil(typeArguments) @@ -18741,11 +18721,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.Get2(base.Name); !ok || s.Flags&ast.SymbolFlagsValue == 0 { if symbols == nil { - symbols = make(ast.SymbolTable) + symbols = ast.NewSymbolTable() } - symbols[base.Name] = base + symbols.Set(base.Name, base) } } } @@ -18758,8 +18738,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 @@ -18768,7 +18748,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 } @@ -18931,7 +18911,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 { @@ -19788,8 +19768,8 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { 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]) + callSignatures := c.getSignaturesOfSymbol(members.Get(ast.InternalSymbolNameCall)) + constructSignatures := c.getSignaturesOfSymbol(members.Get(ast.InternalSymbolNameNew)) indexInfos := c.getIndexInfosOfSymbol(symbol) c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) return @@ -19798,12 +19778,10 @@ 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 { - 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 := ast.NewSymbolTable() + for _, p := range members.Iter() { if p.Flags&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags&ast.SymbolFlagsValueModule != 0 && len(p.Declarations) != 0 && core.Every(p.Declarations, ast.IsAmbientModule)) { + varsOnly.Set(p.Name, p) + } } members = varsOnly } var baseConstructorIndexInfo *IndexInfo @@ -19812,16 +19790,16 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { classType := c.getDeclaredTypeOfClassOrInterface(symbol) baseConstructorType := c.getBaseConstructorTypeOfClass(classType) if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsTypeVariable) != 0 { - members = maps.Clone(members) + members = members.Clone() c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType)) 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) @@ -19844,7 +19822,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) } @@ -19858,9 +19836,9 @@ func (c *Checker) createInstantiatedSymbolTable(symbols []*ast.Symbol, m *TypeMa if len(symbols) == 0 { return nil } - result := make(ast.SymbolTable) + result := ast.NewSymbolTable() for _, symbol := range symbols { - result[symbol.Name] = c.instantiateSymbol(symbol, m) + result.Set(symbol.Name, c.instantiateSymbol(symbol, m)) } return result } @@ -19868,19 +19846,17 @@ func (c *Checker) createInstantiatedSymbolTable(symbols []*ast.Symbol, m *TypeMa // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper, mappingThisOnly bool) ast.SymbolTable { - if len(symbols) == 0 { + if symbols.Len() == 0 { return nil } - result := make(ast.SymbolTable, len(symbols)) - for id, symbol := range symbols { - if c.isNamedMember(symbol, id) { + result := ast.NewSymbolTableWithCapacity(symbols.Len()) + for id, symbol := range symbols.Iter() { if c.isNamedMember(symbol, id) { if mappingThisOnly && isThisless(symbol) { - result[id] = symbol + result.Set(id, symbol) } else { - result[id] = c.instantiateSymbol(symbol, m) + result.Set(id, c.instantiateSymbol(symbol, m)) } - } - } + } } return result } @@ -20021,7 +19997,7 @@ func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature { } func (c *Checker) resolveMappedTypeMembers(t *Type) { - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() var indexInfos []*IndexInfo // Resolve upfront such that recursive references see an empty object type. c.setStructuredTypeMembers(t, nil, nil, nil, nil) @@ -20045,7 +20021,7 @@ 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.nameType = c.getUnionType([]*Type{valueLinks.nameType, propNameType}) mappedLinks := c.mappedSymbolLinks.Get(existingProp) @@ -20075,7 +20051,7 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { prop.Declarations = modifiersProp.Declarations } } - members[propName] = prop + members.Set(propName, prop) } } else if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 { indexKeyType := propNameType @@ -20529,7 +20505,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.members[name] + symbol := resolved.members.Get(name) if symbol != nil && c.symbolIsValue(symbol) { return symbol } @@ -20558,17 +20534,17 @@ func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjec } else { cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache) } - if prop := cache[name]; prop != nil { + if prop := cache.Get(name); prop != nil { return prop } prop := c.createUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment) if prop != nil { - cache[name] = prop + cache.Set(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 augmentedCache.Get(name) == nil { + augmentedCache.Set(name, prop) } } } @@ -21210,15 +21186,13 @@ func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { } func (c *Checker) getNamedMembers(members ast.SymbolTable) []*ast.Symbol { - if len(members) == 0 { + if members.Len() == 0 { return nil } - result := make([]*ast.Symbol, 0, len(members)) - for id, symbol := range members { - if c.isNamedMember(symbol, id) { + result := make([]*ast.Symbol, 0, members.Len()) + for id, symbol := range members.Iter() { if c.isNamedMember(symbol, id) { result = append(result, symbol) - } - } + } } c.sortSymbols(result) return result } @@ -22082,7 +22056,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 || c.getMembersOfSymbol(sym).Len() == 0 && alias == nil { links.resolvedType = c.emptyTypeLiteralType } else { t := c.newObjectType(ObjectFlagsAnonymous, node.Symbol()) @@ -22873,11 +22847,9 @@ 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() { - if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { + for _, symbol := range node.Locals().Iter() { if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { result = append(result, c.getDeclaredTypeOfSymbol(symbol)) - } - } + } } return result } @@ -23137,7 +23109,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 { debug.Assert(ast.GetSourceFileOfNode(member.ValueDeclaration) == ast.GetSourceFileOfNode(rootSymbol.ValueDeclaration)) if location != nil { @@ -23842,7 +23814,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) @@ -23855,11 +23827,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 @@ -23870,7 +23842,7 @@ 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) @@ -25492,7 +25464,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 { @@ -26144,7 +26116,7 @@ func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectTy 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) { @@ -27048,7 +27020,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 } @@ -27116,14 +27088,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 } @@ -29058,10 +29030,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 @@ -29391,7 +29363,7 @@ func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, cont return false }) discriminantMembers := core.Filter(c.getPropertiesOfType(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) @@ -29750,7 +29722,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.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) + return len(resolved.signatures) != 0 || resolved.members.Get("bind") != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { @@ -30245,7 +30217,7 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy // member should more exactly be the kind of (declarationless) symbol we want. // (See #44364 and #45031 for relevant implementation PRs) if metaProp.KeywordToken == ast.KindImportKeyword && node.Text() == "meta" { - return c.getGlobalImportMetaExpressionType().AsObjectType().members["meta"] + return c.getGlobalImportMetaExpressionType().AsObjectType().members.Get("meta") } // no other meta properties are valid syntax, thus no others should have symbols return nil diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index ba40b72a08..341cd0ef4c 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -482,7 +482,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.Iter() { merged := r.checker.getMergedSymbol(s) if merged != s { if len(merged.Declarations) > 0 { @@ -964,7 +964,10 @@ func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitC instanceIndexSymbol := r.checker.getIndexSymbol(sym) var instanceInfos []*IndexInfo if instanceIndexSymbol != nil { - siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym))) + var siblingSymbols []*ast.Symbol + for _, s := range r.checker.getMembersOfSymbol(sym).Iter() { + siblingSymbols = append(siblingSymbols, s) + } instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) } diff --git a/internal/checker/inference.go b/internal/checker/inference.go index 2d8db851cc..ac98d64a18 100644 --- a/internal/checker/inference.go +++ b/internal/checker/inference.go @@ -1037,7 +1037,7 @@ 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)} } - members := make(ast.SymbolTable) + members := ast.NewSymbolTable() limitedConstraint := c.getLimitedConstraint(t) for _, prop := range c.getPropertiesOfType(r.source) { // In case of a reverse mapped type with an intersection constraint, if we were able to @@ -1068,7 +1068,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) } @@ -1158,7 +1158,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 := ast.NewSymbolTable() for _, t := range t.Distributed() { if t.flags&TypeFlagsStringLiteral == 0 { continue @@ -1170,7 +1170,7 @@ func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type { literalProp.Declarations = t.symbol.Declarations literalProp.ValueDeclaration = t.symbol.ValueDeclaration } - members[name] = literalProp + members.Set(name, literalProp) } var indexInfos []*IndexInfo if t.flags&TypeFlagsString != 0 { diff --git a/internal/checker/jsx.go b/internal/checker/jsx.go index 7f0342d76b..476ea09841 100644 --- a/internal/checker/jsx.go +++ b/internal/checker/jsx.go @@ -276,14 +276,14 @@ func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, cont return (initializer == nil || c.isPossiblyDiscriminantValue(initializer)) && c.isDiscriminantProperty(contextualType, symbol.Name) }) discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool { - if s.Flags&ast.SymbolFlagsOptional == 0 || node.Symbol() == nil || len(node.Symbol().Members) == 0 { + if s.Flags&ast.SymbolFlagsOptional == 0 || node.Symbol() == nil { return false } element := node.Parent.Parent 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) @@ -693,9 +693,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 := ast.NewSymbolTable() var attributesSymbol *ast.Symbol attributeParent := openingLikeElement spread := c.emptyJsxObjectType @@ -733,9 +733,9 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme 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 @@ -755,9 +755,9 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme } } else { debug.Assert(attributeDecl.Kind == ast.KindJsxSpreadAttribute) - if len(attributesTable) != 0 { + if attributesTable != nil { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) - attributesTable = make(ast.SymbolTable) + attributesTable = ast.NewSymbolTable() } exprType := c.getReducedType(c.checkExpressionEx(attributeDecl.Expression(), checkMode&CheckModeInferential)) if IsTypeAny(exprType) { @@ -779,7 +779,7 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme } } if !hasSpreadAnyType { - if len(attributesTable) != 0 { + if attributesTable != nil { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) } } @@ -835,8 +835,8 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme childrenPropSymbol.ValueDeclaration = 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 + childPropMap := ast.NewSymbolTable() + childPropMap.Set(jsxChildrenPropertyName, childrenPropSymbol) spread = c.getSpreadType(spread, c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil), attributesSymbol, objectFlags, false /*readonly*/) } } diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index 9f07545c05..83d9e432e4 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -641,17 +641,17 @@ 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.Get2(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 { + exports.Each(func(name string, ex *ast.Symbol) { 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 } - } + }) resultSymbols := slices.Collect(maps.Keys(results)) if len(resultSymbols) > 0 { b.ch.sortSymbols(resultSymbols) @@ -685,8 +685,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) { @@ -993,7 +993,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.Get2(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 diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go index e6394df456..dfd084f712 100644 --- a/internal/checker/nodebuilderscopes.go +++ b/internal/checker/nodebuilderscopes.go @@ -112,21 +112,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.Get2(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 { @@ -143,10 +143,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/internal/checker/services.go b/internal/checker/services.go index caed1fbccc..c0f8573949 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -1,7 +1,6 @@ package checker import ( - "maps" "slices" "github.com/microsoft/typescript-go/internal/ast" @@ -20,7 +19,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 @@ -31,15 +30,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.Get2(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.Iter() { copySymbol(symbol, meaning) } } @@ -47,7 +46,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.Iter() { // Similar condition as in `resolveNameHelper` if ast.GetDeclarationOfKind(symbol, ast.KindExportSpecifier) == nil && ast.GetDeclarationOfKind(symbol, ast.KindNamespaceExport) == nil && @@ -110,7 +109,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) } @@ -119,7 +118,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).Iter() { if !isReservedMemberName(key) { cb(exportedSymbol, key) } @@ -140,7 +139,7 @@ func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb if reducedType.flags&TypeFlagsStructuredType == 0 { return } - for name, symbol := range c.resolveStructuredTypeMembers(reducedType).members { + for name, symbol := range c.resolveStructuredTypeMembers(reducedType).members.Iter() { if c.isNamedMember(symbol, name) { cb(symbol, name) } @@ -198,20 +197,24 @@ 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.Get2(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)) + var result []*ast.Symbol + for _, prop := range props.Iter() { + result = append(result, prop) + } + return result } func (c *Checker) IsUnknownSymbol(symbol *ast.Symbol) bool { @@ -262,12 +265,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.Get2(p.Name); !ok { + propsByName.Set(p.Name, p) } } } @@ -294,7 +297,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 { @@ -833,3 +836,4 @@ func isArrayLiteralOrObjectLiteralDestructuringPattern(node *ast.Node) bool { // [x, [a, b, c] ] = someExpression return isArrayLiteralOrObjectLiteralDestructuringPattern(parent) } + diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index d9741f18a5..445e1c0b83 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -144,13 +144,14 @@ func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *a container.Flags&ast.SymbolFlagsType != 0 && ch.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 { ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { - for _, s := range t { - if s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) { + found := false + t.Each(func(name string, s *ast.Symbol) { + if !found && s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) { firstVariableMatch = s - return true + found = true } - } - return false + }) + return found }) } @@ -265,7 +266,7 @@ func (ch *Checker) getFileSymbolIfFileSymbolExportEqualsContainer(d *ast.Node, c if fileSymbol == nil || fileSymbol.Exports == nil { return nil } - exported, ok := fileSymbol.Exports[ast.InternalSymbolNameExportEquals] + exported, ok := fileSymbol.Exports.Get2(ast.InternalSymbolNameExportEquals) if !ok || exported == nil { return nil } @@ -349,18 +350,18 @@ func (ch *Checker) getAliasForSymbolInContainer(container *ast.Symbol, symbol *a // 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.Get2(ast.InternalSymbolNameExportEquals) if ok && exportEquals != nil && ch.getSymbolIfSameReference(exportEquals, symbol) != nil { return container } } exports := ch.getExportsOfSymbol(container) - quick, ok := exports[symbol.Name] + quick, ok := exports.Get2(symbol.Name) if ok && quick != nil && ch.getSymbolIfSameReference(quick, symbol) != nil { return quick } var candidates []*ast.Symbol - for _, exported := range exports { + for _, exported := range exports.Iter() { if ch.getSymbolIfSameReference(exported, symbol) != nil { candidates = append(candidates, exported) } @@ -466,14 +467,14 @@ func (ch *Checker) trySymbolTable( isLocalNameLookup bool, ) []*ast.Symbol { // If symbol is directly available by its name in the symbol table - res, ok := symbols[ctx.symbol.Name] + res, ok := symbols.Get2(ctx.symbol.Name) if ok && res != nil && ch.isAccessible(ctx, res /*resolvedAliasSymbol*/, nil, ignoreQualification) { return []*ast.Symbol{ctx.symbol} } var candidateChains [][]*ast.Symbol // collect all possible chains to sort them and return the shortest/best - for _, symbolFromSymbolTable := range symbols { + for _, symbolFromSymbolTable := range symbols.Iter() { // for every non-default, non-export= alias symbol in scope, check if it refers to or can chain to the target symbol if symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && symbolFromSymbolTable.Name != ast.InternalSymbolNameExportEquals && @@ -608,7 +609,7 @@ func (ch *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration * qualify := false ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ 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.Get2(symbol.Name) if !ok || res == nil { return false } @@ -693,12 +694,12 @@ func (ch *Checker) someSymbolTableInScope( var table ast.SymbolTable sym := ch.getSymbolOfDeclaration(location) // TODO: Should this filtered table be cached in some way? - for key, memberSymbol := range sym.Members { + for key, memberSymbol := range sym.Members.Iter() { 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, false, false, location) { diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index 10f1c6c0c7..6ca411fb4f 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -330,7 +330,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 { @@ -416,9 +416,9 @@ 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 } @@ -1363,7 +1363,7 @@ func isInNameOfExpressionWithTypeArguments(node *ast.Node) bool { } 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. @@ -1863,11 +1863,11 @@ func introducesArgumentsExoticObject(node *ast.Node) bool { func symbolsToArray(symbols ast.SymbolTable) []*ast.Symbol { var result []*ast.Symbol - for id, symbol := range symbols { + symbols.Each(func(id string, symbol *ast.Symbol) { if !isReservedMemberName(id) { result = append(result, symbol) } - } + }) return result } diff --git a/internal/execute/incremental/affectedfileshandler.go b/internal/execute/incremental/affectedfileshandler.go index 355b58aec8..01273df775 100644 --- a/internal/execute/incremental/affectedfileshandler.go +++ b/internal/execute/incremental/affectedfileshandler.go @@ -223,7 +223,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.Iter() { if exported.Flags&ast.SymbolFlagsConstEnum != 0 { invalidateJsFiles = true break diff --git a/internal/ls/completions.go b/internal/ls/completions.go index 9a1dc9836d..e875fc6975 100644 --- a/internal/ls/completions.go +++ b/internal/ls/completions.go @@ -1429,13 +1429,13 @@ func (l *LanguageService) getCompletionData( if localSymbol != nil { localExports = localSymbol.Exports } - for name, symbol := range localsContainer.Locals() { + localsContainer.Locals().Each(func(name string, symbol *ast.Symbol) { symbols = append(symbols, symbol) - if _, ok := localExports[name]; ok { + if _, ok := localExports.Get2(name); ok { symbolId := ast.GetSymbolId(symbol) symbolToSortTextMap[symbolId] = SortTextOptionalMember } - } + }) return globalsSearchSuccess } diff --git a/internal/ls/definition.go b/internal/ls/definition.go index 5e765fcc0b..eb33fff3e0 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -128,7 +128,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/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 8f099d691f..ce2ba359be 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -694,7 +694,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 := getReferencedSymbolsForModule(program, symbol, exportEquals != nil, sourceFiles, sourceFilesSet) if exportEquals == nil || !sourceFilesSet.Has(moduleSourceFileName) { diff --git a/internal/ls/hover.go b/internal/ls/hover.go index a608fdf024..fecdff43af 100644 --- a/internal/ls/hover.go +++ b/internal/ls/hover.go @@ -117,7 +117,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol } declaration := symbol.ValueDeclaration if symbol.Flags&ast.SymbolFlagsClass != 0 && inConstructorContext(node) { - if s := symbol.Members[ast.InternalSymbolNameConstructor]; s != nil { + if s := symbol.Members.Get(ast.InternalSymbolNameConstructor); s != nil { symbol = s declaration = core.Find(symbol.Declarations, func(d *ast.Node) bool { return ast.IsConstructorDeclaration(d) || ast.IsConstructSignatureDeclaration(d) diff --git a/internal/modulespecifiers/specifiers.go b/internal/modulespecifiers/specifiers.go index 1f8e26b040..df85a4aa76 100644 --- a/internal/modulespecifiers/specifiers.go +++ b/internal/modulespecifiers/specifiers.go @@ -106,7 +106,7 @@ func tryGetModuleNameFromAmbientModule(moduleSymbol *ast.Symbol, checker Checker continue } - sym, ok := possibleContainer.Symbol().Exports[ast.InternalSymbolNameExportEquals] + sym, ok := possibleContainer.Symbol().Exports.Get2(ast.InternalSymbolNameExportEquals) if !ok || sym == nil { continue } diff --git a/internal/printer/namegenerator.go b/internal/printer/namegenerator.go index 01a5d4b079..e962fea62a 100644 --- a/internal/printer/namegenerator.go +++ b/internal/printer/namegenerator.go @@ -362,7 +362,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.Get2(name); ok && local.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue|ast.SymbolFlagsAlias) != 0 { return false } } From f533d2e29e51e230b763661ef5b934ae651d0260 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Fri, 26 Sep 2025 16:09:00 -0700 Subject: [PATCH 13/28] ughhh --- cmd/tsgo/api.go | 2 +- internal/api/proto.go | 2 +- internal/api/server.go | 23 +- internal/ast/ast.go | 4 + internal/ast/symbol.go | 299 +++++++++++++++++- internal/binder/binder.go | 25 +- internal/binder/nameresolver.go | 10 +- internal/checker/checker.go | 296 +++++++++++------ internal/checker/services.go | 6 +- internal/checker/symbolaccessibility.go | 9 +- .../execute/incremental/programtosnapshot.go | 2 +- internal/ls/api.go | 9 +- internal/ls/autoimports.go | 2 +- internal/module/types.go | 24 +- internal/tsoptions/enummaps.go | 2 +- 15 files changed, 570 insertions(+), 145 deletions(-) diff --git a/cmd/tsgo/api.go b/cmd/tsgo/api.go index 7493a2ed94..ae345a0920 100644 --- a/cmd/tsgo/api.go +++ b/cmd/tsgo/api.go @@ -20,7 +20,7 @@ func runAPI(args []string) int { } defaultLibraryPath := bundled.LibPath() - + logEnabled := os.Getenv("TSGO_LOG_ENABLED") == "1" s := api.NewServer(&api.ServerOptions{ diff --git a/internal/api/proto.go b/internal/api/proto.go index 256f21236f..47cd241da0 100644 --- a/internal/api/proto.go +++ b/internal/api/proto.go @@ -101,7 +101,7 @@ var unmarshalers = map[Method]func([]byte) (any, error){ MethodGetSymbolsAtLocations: unmarshallerFor[GetSymbolsAtLocationsParams], MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams], MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams], - MethodGetDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetDiagnostics: unmarshallerFor[GetDiagnosticsParams], } type ConfigureParams struct { diff --git a/internal/api/server.go b/internal/api/server.go index 327eb005f8..4b024bf689 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -210,10 +210,13 @@ func (r *resolverWrapper) GetPackageJsonScopeIfApplicable(path string) *packagej panic(err) } if len(result) > 0 { - var res PackageJsonIfApplicable + var res *PackageJsonIfApplicable if err := json.Unmarshal(result, &res); err != nil { panic(err) } + if res == nil { + return nil + } contents, err := packagejson.Parse([]byte(res.Contents)) if err != nil { panic(err) @@ -221,7 +224,7 @@ func (r *resolverWrapper) GetPackageJsonScopeIfApplicable(path string) *packagej return &packagejson.InfoCacheEntry{ PackageDirectory: res.PackageDirectory, DirectoryExists: res.DirectoryExists, - Contents: &packagejson.PackageJson{ + Contents: &packagejson.PackageJson{ Fields: contents, }, } @@ -254,9 +257,9 @@ func (r *resolverWrapper) GetPackageScopeForPath(directory string) *packagejson. func (r *resolverWrapper) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode, redirectedReference module.ResolvedProjectReference) (*module.ResolvedModule, []string) { if r.server.CallbackEnabled(CallbackResolveModuleName) { result, err := r.server.call("resolveModuleName", map[string]any{ - "moduleName": moduleName, - "containingFile": containingFile, - "resolutionMode": resolutionMode, + "moduleName": moduleName, + "containingFile": containingFile, + "resolutionMode": resolutionMode, "redirectedReference": redirectedReference, }) if err != nil { @@ -278,9 +281,9 @@ func (r *resolverWrapper) ResolveTypeReferenceDirective(typeReferenceDirectiveNa if r.server.CallbackEnabled(CallbackResolveTypeReferenceDirective) { result, err := r.server.call("resolveTypeReferenceDirective", map[string]any{ "typeReferenceDirectiveName": typeReferenceDirectiveName, - "containingFile": containingFile, - "resolutionMode": resolutionMode, - "redirectedReference": redirectedReference, + "containingFile": containingFile, + "resolutionMode": resolutionMode, + "redirectedReference": redirectedReference, }) if err != nil { panic(err) @@ -299,7 +302,7 @@ func (r *resolverWrapper) ResolveTypeReferenceDirective(typeReferenceDirectiveNa func (r *resolverWrapper) GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind { if r.server.CallbackEnabled(CallbackGetImpliedNodeFormatForFile) { result, err := r.server.call("getImpliedNodeFormatForFile", map[string]any{ - "fileName": path, + "fileName": path, "packageJsonType": packageJsonType, }) if err != nil { @@ -331,7 +334,7 @@ func NewServer(options *ServerOptions) *Server { fs: bundled.WrapFS(osvfs.FS()), defaultLibraryPath: options.DefaultLibraryPath, } - + var logger logging.Logger if options.LogEnabled { logger = logging.NewLogger(options.Err) diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 1a6a9d8283..8aed54be47 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -123,6 +123,9 @@ func newNode(kind Kind, data nodeData, hooks NodeFactoryHooks) *Node { n.Loc = core.UndefinedTextRange() n.Kind = kind n.data = data + if data.LocalsContainerData() != nil && data.LocalsContainerData().Locals == nil { + data.LocalsContainerData().Locals = NewSymbolTable() + } if hooks.OnCreate != nil { hooks.OnCreate(n) } @@ -10438,6 +10441,7 @@ func (f *NodeFactory) NewSourceFile(opts SourceFileParseOptions, text string, st data.text = text data.Statements = statements data.EndOfFileToken = endOfFileToken + data.Locals = NewSymbolTable() return f.newNode(KindSourceFile, data) } diff --git a/internal/ast/symbol.go b/internal/ast/symbol.go index a16789afe5..8c415a01b9 100644 --- a/internal/ast/symbol.go +++ b/internal/ast/symbol.go @@ -3,9 +3,11 @@ package ast import ( "iter" "maps" + "strings" "sync/atomic" "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/tspath" ) // Symbol @@ -36,12 +38,11 @@ type SymbolTable interface { Delete(name string) Keys() iter.Seq[string] Values() iter.Seq[*Symbol] - Each(func(name string, symbol *Symbol) ) + Each(func(name string, symbol *Symbol)) Iter() iter.Seq2[string, *Symbol] Len() int Clone() SymbolTable Find(predicate func(*Symbol) bool) *Symbol - } type SymbolMap struct { @@ -66,7 +67,7 @@ func (m *SymbolMap) Len() int { } func (m *SymbolMap) Iter() iter.Seq2[string, *Symbol] { - return func(yield func (string, *Symbol) bool) { + return func(yield func(string, *Symbol) bool) { for name, symbol := range m.m { if !yield(name, symbol) { return @@ -93,7 +94,7 @@ func (m *SymbolMap) Delete(name string) { } func (m *SymbolMap) Keys() iter.Seq[string] { - return func(yield func (string) bool) { + return func(yield func(string) bool) { for name := range m.m { if !yield(name) { return @@ -103,7 +104,7 @@ func (m *SymbolMap) Keys() iter.Seq[string] { } func (m *SymbolMap) Values() iter.Seq[*Symbol] { - return func(yield func (*Symbol) bool) { + return func(yield func(*Symbol) bool) { for _, symbol := range m.m { if !yield(symbol) { return @@ -112,7 +113,7 @@ func (m *SymbolMap) Values() iter.Seq[*Symbol] { } } -func (m *SymbolMap) Each(fn func(name string, symbol *Symbol) ) { +func (m *SymbolMap) Each(fn func(name string, symbol *Symbol)) { for name, symbol := range m.m { fn(name, symbol) } @@ -130,7 +131,6 @@ func NewSymbolTableFromMap(m map[string]*Symbol) SymbolTable { return &SymbolMap{m: m} } - const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName const ( @@ -162,3 +162,288 @@ func SymbolName(symbol *Symbol) string { } return symbol.Name } + +type CombinedSymbolTable struct { + firstTable SymbolTable + secondTable SymbolTable +} + +// Clone implements SymbolTable. +func (c *CombinedSymbolTable) Clone() SymbolTable { + return &CombinedSymbolTable{ + firstTable: c.firstTable.Clone(), + secondTable: c.secondTable.Clone(), + } +} + +// Delete implements SymbolTable. +func (c *CombinedSymbolTable) Delete(name string) { + if c.firstTable.Get(name) != nil { + c.firstTable.Delete(name) + } else { + c.secondTable.Delete(name) + } +} + +// Each implements SymbolTable. +func (c *CombinedSymbolTable) Each(fn func(name string, symbol *Symbol)) { + c.firstTable.Each(func(name string, symbol *Symbol) { + fn(name, symbol) + }) + c.secondTable.Each(func(name string, symbol *Symbol) { + fn(name, symbol) + }) +} + +// Find implements SymbolTable. +func (c *CombinedSymbolTable) Find(predicate func(*Symbol) bool) *Symbol { + if c.firstTable.Find(predicate) != nil { + return c.firstTable.Find(predicate) + } + return c.secondTable.Find(predicate) +} + +// Get implements SymbolTable. +func (c *CombinedSymbolTable) Get(name string) *Symbol { + if c.firstTable.Get(name) != nil { + return c.firstTable.Get(name) + } + return c.secondTable.Get(name) +} + +// Get2 implements SymbolTable. +func (c *CombinedSymbolTable) Get2(name string) (*Symbol, bool) { + if value, ok := c.firstTable.Get2(name); ok { + return value, ok + } + return c.secondTable.Get2(name) +} + +// Iter implements SymbolTable. +func (c *CombinedSymbolTable) Iter() iter.Seq2[string, *Symbol] { + seen := make(map[string]struct{}) + return func(yield func(string, *Symbol) bool) { + c.firstTable.Iter()(func(name string, symbol *Symbol) bool { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + return yield(name, symbol) + } + return true + }) + c.secondTable.Iter()(func(name string, symbol *Symbol) bool { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + return yield(name, symbol) + } + return true + }) + } +} + +// Keys implements SymbolTable. +func (c *CombinedSymbolTable) Keys() iter.Seq[string] { + return func(yield func(string) bool) { + c.Iter()(func(name string, symbol *Symbol) bool { + return yield(name) + }) + } +} + +// Len implements SymbolTable. +func (c *CombinedSymbolTable) Len() int { + len := 0 + for k := range c.Iter() { + _ = k + len++ + } + return len +} + +// Set implements SymbolTable. +func (c *CombinedSymbolTable) Set(name string, symbol *Symbol) { + c.firstTable.Set(name, symbol) +} + +// Values implements SymbolTable. +func (c *CombinedSymbolTable) Values() iter.Seq[*Symbol] { + return func(yield func(*Symbol) bool) { + c.Iter()(func(name string, symbol *Symbol) bool { + return yield(symbol) + }) + } +} + +var _ SymbolTable = (*CombinedSymbolTable)(nil) + +var NodeOnlyGlobalNames = collections.NewSetFromItems( + "__dirname", + "__filename", + "buffer", + "Buffer", + "BufferConstructor", + "BufferEncoding", + "clearImmediate", + "clearInterval", + "clearTimeout", + "console", + "Console", + "crypto", + "ErrorConstructor", + "gc", + "Global", + "localStorage", + "queueMicrotask", + "RequestInit", + "ResponseInit", + "sessionStorage", + "setImmediate", + "setInterval", + "setTimeout", +) + +var TypesNodeIgnorableNames = collections.NewSetFromItems( + "AbortController", + "AbortSignal", + "AsyncIteratorObject", + "atob", + "Blob", + "BroadcastChannel", + "btoa", + "ByteLengthQueuingStrategy", + "CloseEvent", + "CompressionStream", + "CountQueuingStrategy", + "CustomEvent", + "DecompressionStream", + "Disposable", + "DOMException", + "Event", + "EventSource", + "EventTarget", + "fetch", + "File", + "Float32Array", + "Float64Array", + "FormData", + "Headers", + "ImportMeta", + "MessageChannel", + "MessageEvent", + "MessagePort", + "performance", + "PerformanceEntry", + "PerformanceMark", + "PerformanceMeasure", + "ReadableByteStreamController", + "ReadableStream", + "ReadableStreamBYOBReader", + "ReadableStreamBYOBRequest", + "ReadableStreamDefaultController", + "ReadableStreamDefaultReader", + "ReadonlyArray", + "Request", + "Response", + "Storage", + "TextDecoder", + "TextDecoderStream", + "TextEncoder", + "TextEncoderStream", + "TransformStream", + "TransformStreamDefaultController", + "URL", + "URLPattern", + "URLSearchParams", + "WebSocket", + "WritableStream", + "WritableStreamDefaultController", + "WritableStreamDefaultWriter", +) + +type DenoForkContext struct { + globals SymbolTable + nodeGlobals SymbolTable + combinedGlobals SymbolTable + mergeSymbol func(target *Symbol, source *Symbol, unidirectional bool) *Symbol + isNodeSourceFile func(path tspath.Path) bool +} + +func NewDenoForkContext( + globals SymbolTable, + nodeGlobals SymbolTable, + mergeSymbol func(target *Symbol, source *Symbol, unidirectional bool) *Symbol, + isNodeSourceFile func(path tspath.Path) bool, +) *DenoForkContext { + return &DenoForkContext{ + globals: globals, + nodeGlobals: nodeGlobals, + combinedGlobals: &CombinedSymbolTable{ + firstTable: nodeGlobals, + secondTable: globals, + }, + mergeSymbol: mergeSymbol, + isNodeSourceFile: isNodeSourceFile, + } +} + +func (c *DenoForkContext) GetGlobalsForName(name string) SymbolTable { + if NodeOnlyGlobalNames.Has(name) { + return c.nodeGlobals + } else { + return c.globals + } +} + +func isTypesNodePkgPath(path tspath.Path) bool { + return strings.HasSuffix(string(path), ".d.ts") && strings.Contains(string(path), "/@types/node/") +} + +func symbolHasAnyTypesNodePkgDecl(symbol *Symbol, hasNodeSourceFile func(*Node) bool) bool { + if symbol == nil || symbol.Declarations == nil { + return false + } + for _, decl := range symbol.Declarations { + sourceFile := GetSourceFileOfNode(decl) + if sourceFile != nil && hasNodeSourceFile(decl) && isTypesNodePkgPath(sourceFile.Path()) { + return true + } + } + return false +} + +func (c *DenoForkContext) MergeGlobalSymbolTable(node *Node, source SymbolTable, unidirectional bool) { + sourceFile := GetSourceFileOfNode(node) + isNodeFile := c.HasNodeSourceFile(node) + isTypesNodeSourceFile := isNodeFile && isTypesNodePkgPath(sourceFile.Path()) + + for id, sourceSymbol := range source.Iter() { + var target SymbolTable + if isNodeFile { + target = c.GetGlobalsForName(id) + } else { + target = c.globals + } + targetSymbol := target.Get(id) + if isTypesNodeSourceFile && targetSymbol != nil && TypesNodeIgnorableNames.Has(id) && !symbolHasAnyTypesNodePkgDecl(targetSymbol, c.HasNodeSourceFile) { + continue + } + target.Set(id, sourceSymbol) + } +} + +func (c *DenoForkContext) CombinedGlobals() SymbolTable { + return c.combinedGlobals +} + +func (c *DenoForkContext) HasNodeSourceFile(node *Node) bool { + if node == nil { + return false + } + sourceFile := GetSourceFileOfNode(node) + if sourceFile == nil { + return false + } + if c.isNodeSourceFile(sourceFile.Path()) { + return true + } + return false +} diff --git a/internal/binder/binder.go b/internal/binder/binder.go index e21aca2daf..555e4a19bc 100644 --- a/internal/binder/binder.go +++ b/internal/binder/binder.go @@ -134,6 +134,10 @@ func (b *Binder) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol { result := b.symbolPool.New() result.Flags = flags result.Name = name + result.Members = ast.NewSymbolTable() + result.Exports = ast.NewSymbolTable() + result.GlobalExports = ast.NewSymbolTable() + return result } @@ -1220,28 +1224,37 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { if ast.IsPropertyAccessExpression(node) && node.AsPropertyAccessExpression().Expression.Kind == ast.KindThisKeyword || ast.IsElementAccessExpression(node) && node.AsElementAccessExpression().Expression.Kind == ast.KindThisKeyword { if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { - if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbolTable.Get(name.Text()) - } + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + 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.Get(name.Text()) - } + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + return symbol.Exports.Get(name.Text()) + } } return nil } func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol { + if container == nil { + panic("container is nil in lookupName: " + name) + } if localsContainer := container.LocalsContainerData(); localsContainer != nil { + if localsContainer.Locals == nil { + localsContainer.Locals = ast.NewSymbolTable() + } if local := localsContainer.Locals.Get(name); local != nil { return core.OrElse(local.ExportSymbol, local) } } if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil { + if declaration.Symbol.Exports == nil { + declaration.Symbol.Exports = ast.NewSymbolTable() + } return declaration.Symbol.Exports.Get(name) } return nil diff --git a/internal/binder/nameresolver.go b/internal/binder/nameresolver.go index e6b9965715..aeb206b6dd 100644 --- a/internal/binder/nameresolver.go +++ b/internal/binder/nameresolver.go @@ -10,7 +10,9 @@ type NameResolver struct { CompilerOptions *core.CompilerOptions GetSymbolOfDeclaration func(node *ast.Node) *ast.Symbol Error func(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic - Globals ast.SymbolTable + DenoGlobals ast.SymbolTable + NodeGlobals ast.SymbolTable + DenoContext *ast.DenoForkContext ArgumentsSymbol *ast.Symbol RequireSymbol *ast.Symbol GetModuleSymbol func(sourceFile *ast.Node) *ast.Symbol @@ -318,7 +320,11 @@ loop: return r.GetModuleSymbol(lastLocation) } if !excludeGlobals { - result = r.lookup(r.Globals, name, meaning|ast.SymbolFlagsGlobalLookup) + if r.DenoContext.HasNodeSourceFile(lastLocation) { + result = r.lookup(r.NodeGlobals, name, meaning|ast.SymbolFlagsGlobalLookup) + } else { + result = r.lookup(r.DenoGlobals, name, meaning|ast.SymbolFlagsGlobalLookup) + } } } if result == nil { diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 41fe913ad3..64ba429f9b 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -547,7 +547,7 @@ type Program interface { type Host interface { modulespecifiers.ModuleSpecifierGenerationHost - + IsNodeSourceFile(path tspath.Path) bool } @@ -590,7 +590,8 @@ type Checker struct { canCollectSymbolAliasAccessibilityData bool wasCanceled bool arrayVariances []VarianceFlags - globals ast.SymbolTable + denoGlobals ast.SymbolTable + nodeGlobals ast.SymbolTable evaluate evaluator.Evaluator stringLiteralTypes map[string]*Type numberLiteralTypes map[jsnum.Number]*Type @@ -622,7 +623,9 @@ type Checker struct { resolvingSymbol *ast.Symbol unresolvedSymbols map[string]*ast.Symbol errorTypes map[string]*Type - globalThisSymbol *ast.Symbol + denoGlobalThisSymbol *ast.Symbol + nodeGlobalThisSymbol *ast.Symbol + denoForkContext *ast.DenoForkContext 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[string]*Type @@ -858,6 +861,8 @@ type Checker struct { activeTypeMappersCaches []map[string]*Type ambientModulesOnce sync.Once ambientModules []*ast.Symbol + nodeAmbientModulesOnce sync.Once + nodeAmbientModules []*ast.Symbol } func NewChecker(program Program) *Checker { @@ -888,7 +893,8 @@ func NewChecker(program Program) *Checker { c.exactOptionalPropertyTypes = c.compilerOptions.ExactOptionalPropertyTypes == core.TSTrue c.canCollectSymbolAliasAccessibilityData = c.compilerOptions.VerbatimModuleSyntax.IsFalseOrUnknown() c.arrayVariances = []VarianceFlags{VarianceFlagsCovariant} - c.globals = ast.NewSymbolTableWithCapacity(countGlobalSymbols(c.files)) + c.denoGlobals = ast.NewSymbolTableWithCapacity(countGlobalSymbols(c.files)) + c.nodeGlobals = 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) @@ -919,9 +925,14 @@ func NewChecker(program Program) *Checker { c.resolvingSymbol = c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameResolving) c.unresolvedSymbols = make(map[string]*ast.Symbol) c.errorTypes = make(map[string]*Type) - c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) - c.globalThisSymbol.Exports = c.globals - c.globals.Set(c.globalThisSymbol.Name, c.globalThisSymbol) + c.denoGlobalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) + c.denoGlobalThisSymbol.Exports = c.denoGlobals + c.denoGlobals.Set(c.denoGlobalThisSymbol.Name, c.denoGlobalThisSymbol) + c.denoForkContext = ast.NewDenoForkContext(c.denoGlobals, c.nodeGlobals, c.mergeSymbol, c.program.IsNodeSourceFile) + c.nodeGlobalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) + c.nodeGlobalThisSymbol.Exports = c.denoForkContext.CombinedGlobals() + c.nodeGlobals.Set(c.nodeGlobalThisSymbol.Name, c.nodeGlobalThisSymbol) + c.nodeGlobals.Set(c.nodeGlobalThisSymbol.Name, c.nodeGlobalThisSymbol) c.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve c.tupleTypes = make(map[string]*Type) @@ -1253,20 +1264,36 @@ func (c *Checker) initializeIterationResolvers() { } } +func (c *Checker) isNodeSourceFile(file *ast.SourceFile) bool { + return c.program.IsNodeSourceFile(file.Path()) +} + +func (c *Checker) globalsForFile(file *ast.SourceFile) ast.SymbolTable { + if c.isNodeSourceFile(file) { + return c.nodeGlobals + } + return c.denoGlobals +} + func (c *Checker) initializeChecker() { // Initialize global symbol table augmentations := make([][]*ast.Node, 0, len(c.files)) for _, file := range c.files { if !ast.IsExternalOrCommonJSModule(file) { - c.mergeSymbolTable(c.globals, file.Locals, false, nil) + c.denoForkContext.MergeGlobalSymbolTable(&file.Node, file.Locals, false /*unidirectional*/) } c.patternAmbientModules = append(c.patternAmbientModules, file.PatternAmbientModules...) augmentations = append(augmentations, file.ModuleAugmentations) + globals := c.globalsForFile(file) + if file.Symbol != nil { + if file.Symbol.GlobalExports == nil { + file.Symbol.GlobalExports = ast.NewSymbolTable() + } // Merge in UMD exports with first-in-wins semantics (see #9771) for name, symbol := range file.Symbol.GlobalExports.Iter() { - if _, ok := c.globals.Get2(name); !ok { - c.globals.Set(name, symbol) + if _, ok := globals.Get2(name); !ok { + globals.Set(name, symbol) } } } @@ -1290,7 +1317,8 @@ func (c *Checker) initializeChecker() { c.valueSymbolLinks.Get(c.undefinedSymbol).resolvedType = c.undefinedWideningType c.valueSymbolLinks.Get(c.argumentsSymbol).resolvedType = c.getGlobalType("IArguments", 0 /*arity*/, true /*reportErrors*/) c.valueSymbolLinks.Get(c.unknownSymbol).resolvedType = c.errorType - c.valueSymbolLinks.Get(c.globalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol) + c.valueSymbolLinks.Get(c.denoGlobalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.denoGlobalThisSymbol) + c.valueSymbolLinks.Get(c.nodeGlobalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.nodeGlobalThisSymbol) // Initialize special types c.globalArrayType = c.getGlobalType("Array", 1 /*arity*/, true /*reportErrors*/) c.globalObjectType = c.getGlobalType("Object", 0 /*arity*/, true /*reportErrors*/) @@ -1324,7 +1352,6 @@ func (c *Checker) initializeChecker() { } } - func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { moduleNode := moduleName.Parent moduleAugmentation := moduleNode.AsModuleDeclaration() @@ -1335,7 +1362,7 @@ func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { return } if ast.IsGlobalScopeAugmentation(moduleNode) { - c.mergeSymbolTable(c.globals, moduleAugmentation.Symbol.Exports, false /*unidirectional*/, nil /*parent*/) + c.denoForkContext.MergeGlobalSymbolTable(moduleNode, moduleAugmentation.Symbol.Exports, false /*unidirectional*/) } else { // find a module that about to be augmented // do not validate names of augmentations that are defined in ambient context @@ -1382,15 +1409,18 @@ func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { name := c.undefinedSymbol.Name - targetSymbol := c.globals.Get(name) - if targetSymbol != nil { - for _, declaration := range targetSymbol.Declarations { - if !ast.IsTypeDeclaration(declaration) { - c.diagnostics.Add(createDiagnosticForNode(declaration, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, name)) + for _, globals := range []ast.SymbolTable{c.denoGlobals, c.nodeGlobals} { + targetSymbol := globals.Get(name) + if targetSymbol != nil { + for _, declaration := range targetSymbol.Declarations { + if !ast.IsTypeDeclaration(declaration) { + c.diagnostics.Add(createDiagnosticForNode(declaration, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, name)) + } } + } else { + globals.Set(name, c.undefinedSymbol) } - } else { - c.globals.Set(name, c.undefinedSymbol) + } } @@ -1399,7 +1429,9 @@ func (c *Checker) createNameResolver() *binder.NameResolver { CompilerOptions: c.compilerOptions, GetSymbolOfDeclaration: c.getSymbolOfDeclaration, Error: c.error, - Globals: c.globals, + DenoGlobals: c.denoGlobals, + NodeGlobals: c.nodeGlobals, + DenoContext: c.denoForkContext, ArgumentsSymbol: c.argumentsSymbol, RequireSymbol: c.requireSymbol, GetModuleSymbol: c.getModuleSymbol, @@ -1418,7 +1450,9 @@ func (c *Checker) createNameResolverForSuggestion() *binder.NameResolver { CompilerOptions: c.compilerOptions, GetSymbolOfDeclaration: c.getSymbolOfDeclaration, Error: c.error, - Globals: c.globals, + DenoGlobals: c.denoGlobals, + NodeGlobals: c.nodeGlobals, + DenoContext: c.denoForkContext, ArgumentsSymbol: c.argumentsSymbol, RequireSymbol: c.requireSymbol, GetModuleSymbol: c.getModuleSymbol, @@ -1767,7 +1801,14 @@ func (c *Checker) onSuccessfullyResolvedSymbol(errorLocation *ast.Node, result * // Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax') // here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'. if c.compilerOptions.IsolatedModules == core.TSTrue && result != nil && isInExternalModule && (meaning&ast.SymbolFlagsValue) == ast.SymbolFlagsValue { - isGlobal := c.getSymbol(c.globals, name, meaning) == result + isNodeFile := c.denoForkContext.HasNodeSourceFile(lastLocation) + var fileGlobals ast.SymbolTable + if isNodeFile { + fileGlobals = c.nodeGlobals + } else { + fileGlobals = c.denoGlobals + } + isGlobal := c.getSymbol(fileGlobals, name, meaning) == result var nonValueSymbol *ast.Symbol if isGlobal && ast.IsSourceFile(lastLocation) { nonValueSymbol = c.getSymbol(lastLocation.Locals(), name, ^ast.SymbolFlagsValue) @@ -4043,9 +4084,11 @@ func (c *Checker) checkCatchClause(node *ast.Node) { } else { blockLocals := node.AsCatchClause().Block.Locals() if blockLocals != nil { - for caughtName := range node.Locals().Iter() { if blockLocal := blockLocals.Get(caughtName); blockLocal != nil && blockLocal.ValueDeclaration != nil && blockLocal.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { + for caughtName := range node.Locals().Iter() { + 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) - } } + } + } } } } @@ -4791,9 +4834,11 @@ func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.No return true } seen := make(map[string]InheritanceInfo) - for id, p := range c.resolveDeclaredMembers(t).declaredMembers.Iter() { if c.isNamedMember(p, id) { + for id, p := range c.resolveDeclaredMembers(t).declaredMembers.Iter() { + if c.isNamedMember(p, id) { seen[p.Name] = InheritanceInfo{prop: p, containingType: t} - } } + } + } identical := true for _, base := range baseTypes { properties := c.getPropertiesOfType(c.getTypeWithThisArgument(base, t.AsInterfaceType().thisType, false)) @@ -5300,7 +5345,7 @@ func (c *Checker) checkExportSpecifier(node *ast.Node) { } // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false) - if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { + if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.denoGlobalThisSymbol || symbol == c.nodeGlobalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { c.error(exportedName, diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, exportedName.Text()) } else { c.markLinkedReferences(node, ReferenceHintExportSpecifier, nil /*propSymbol*/, nil /*parentType*/) @@ -5422,37 +5467,41 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } } // Checks for export * conflicts - for id, symbol := range c.getExportsOfModule(moduleSymbol).Iter() { if id == ast.InternalSymbolNameExportStar { + for id, symbol := range c.getExportsOfModule(moduleSymbol).Iter() { + if id == ast.InternalSymbolNameExportStar { continue } -// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. -// (TS Exceptions: namespaces, function overloads, enums, and interfaces) -if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 { + // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. + // (TS Exceptions: namespaces, function overloads, enums, and interfaces) + if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 { continue } -exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool { + exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool { return isNotOverload(d) && !ast.IsAccessor(d) && !ast.IsInterfaceDeclaration(d) }) -if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 { + if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 { // it is legal to merge type alias with other values // so count should be either 1 (just type alias) or 2 (type alias + merged value) continue } -if exportedDeclarationsCount > 1 { + if exportedDeclarationsCount > 1 { for _, declaration := range symbol.Declarations { if isNotOverload(declaration) { c.error(declaration, diagnostics.Cannot_redeclare_exported_variable_0, id) } } - } } + } + } links.exportsChecked = true } } func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool { - for id := range moduleSymbol.Exports.Iter() { if id != ast.InternalSymbolNameExportEquals { + for id := range moduleSymbol.Exports.Iter() { + if id != ast.InternalSymbolNameExportEquals { return true - } } + } + } return false } @@ -6750,12 +6799,13 @@ 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().Iter() { referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds -if local.Flags&ast.SymbolFlagsTypeParameter != 0 && (local.Flags&ast.SymbolFlagsVariable == 0 || referenceKinds&ast.SymbolFlagsVariable != 0) || + for _, local := range node.Locals().Iter() { + referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds + 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) { continue } -for _, declaration := range local.Declarations { + for _, declaration := range local.Declarations { switch { case ast.IsVariableDeclaration(declaration) || ast.IsParameter(declaration) || ast.IsBindingElement(declaration): variableParents.Add(ast.GetRootDeclaration(declaration).Parent) @@ -6772,7 +6822,8 @@ for _, declaration := range local.Declarations { c.reportUnusedLocal(declaration, ast.SymbolName(local)) } } - } } + } + } for declaration := range variableParents.Keys() { if ast.IsVariableDeclarationList(declaration) { c.reportUnusedVariables(declaration) @@ -10811,8 +10862,8 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l indexInfo = c.getApplicableIndexInfoForName(apparentType, right.Text()) } if indexInfo == nil { - if leftType.symbol == c.globalThisSymbol { - globalSymbol := c.globalThisSymbol.Exports.Get(right.Text()) + if leftType.symbol == c.denoGlobalThisSymbol { + globalSymbol := c.denoGlobalThisSymbol.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 { @@ -10820,6 +10871,9 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l } return c.anyType } + if leftType.symbol == c.nodeGlobalThisSymbol { + return c.anyType + } if right.Text() != "" && !c.checkAndReportErrorForExtendingInterface(node) { c.reportNonexistentProperty(right, core.IfElse(isThisTypeParameter(leftType), apparentType, leftType)) } @@ -11575,7 +11629,7 @@ func (c *Checker) checkThisExpression(node *ast.Node) *Type { } t := c.TryGetThisTypeAtEx(node, true /*includeGlobalThis*/, container) if c.noImplicitThis { - globalThisType := c.getTypeOfSymbol(c.globalThisSymbol) + globalThisType := c.getTypeOfSymbol(c.denoGlobalThisSymbol) if t == globalThisType && capturedByArrowFunction { c.error(node, diagnostics.The_containing_arrow_function_captures_the_global_value_of_this) } else if t == nil { @@ -11636,7 +11690,10 @@ func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, con return c.undefinedType } if includeGlobalThis { - return c.getTypeOfSymbol(c.globalThisSymbol) + if c.denoForkContext.HasNodeSourceFile(container) { + return c.getTypeOfSymbol(c.nodeGlobalThisSymbol) + } + return c.getTypeOfSymbol(c.denoGlobalThisSymbol) } } return nil @@ -13412,6 +13469,9 @@ func (c *Checker) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol { result := c.symbolPool.New() result.Flags = flags | ast.SymbolFlagsTransient result.Name = name + result.Members = ast.NewSymbolTable() + result.Exports = ast.NewSymbolTable() + result.GlobalExports = ast.NewSymbolTable() return result } @@ -13434,6 +13494,14 @@ func (c *Checker) newProperty(name string, t *Type) *ast.Symbol { } func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTable) ast.SymbolTable { + if first == nil && second == nil { + return ast.NewSymbolTable() + } else if first == nil { + return second + } else if second == nil { + return first + } + if first.Len() == 0 { return second } @@ -13446,34 +13514,44 @@ func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTa return combined } +// func nodeGlobalsSymbolTable(nodeGlobals ast.SymbolTable, denoGlobals ast.SymbolTable) ast.SymbolTable { + +// return nil +// } + +// type CombinedSymbolTable struct { +// nodeGlobals ast.SymbolTable +// denoGlobals ast.SymbolTable +// } + +// var _ ast.SymbolTable = (*CombinedSymbolTable)(nil) // func (c *Checker) mergeGlobalSymbolTable(node *ast.Node, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { -// // sourceFile := ast.GetSourceFileOfNode(node) -// // // isNodeFile := c.program.IsNodeSourceFile(sourceFile.Path()) -// // for id, sourceSymbol := range source { -// // target := c.globals -// // targetSymbol := target[id] -// // // var merged *ast.Symbol -// // if targetSymbol != nil { -// // merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) -// // } else { -// // merged = c.getMergedSymbol(sourceSymbol) -// // } -// // } - - - +// sourceFile := ast.GetSourceFileOfNode(node) +// // isNodeFile := c.program.IsNodeSourceFile(sourceFile.Path()) +// for id, sourceSymbol := range source { +// target := c.globals +// targetSymbol := target[id] +// // var merged *ast.Symbol +// if targetSymbol != nil { +// merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) +// } else { +// merged = c.getMergedSymbol(sourceSymbol) +// } +// } + // } func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { - for id, sourceSymbol := range source.Iter() { targetSymbol := target.Get(id) -var merged *ast.Symbol -if targetSymbol != nil { + for id, sourceSymbol := range source.Iter() { + targetSymbol := target.Get(id) + var merged *ast.Symbol + if targetSymbol != nil { merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) } else { merged = c.getMergedSymbol(sourceSymbol) } -if mergedParent != nil && targetSymbol != nil { + if mergedParent != nil && targetSymbol != nil { // If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge // of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent: // @@ -13493,7 +13571,8 @@ if mergedParent != nil && targetSymbol != nil { merged.Parent = mergedParent } } -target.Set(id, merged) } + target.Set(id, merged) + } } /** @@ -13542,7 +13621,7 @@ func (c *Checker) mergeSymbol(target *ast.Symbol, source *ast.Symbol, unidirecti // Do not report an error when merging `var globalThis` with the built-in `globalThis`, // as we will already report a "Declaration name conflicts..." error, and this error // won't make much sense. - if target != c.globalThisSymbol { + if target != c.denoGlobalThisSymbol && target != c.nodeGlobalThisSymbol { c.error(ast.GetNameOfDeclaration(getFirstDeclaration(source)), diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, c.symbolToString(target)) } } else { @@ -14842,7 +14921,7 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool if tspath.IsExternalModuleNameRelative(moduleName) { return nil } - symbol := c.getSymbol(c.globals, "\""+moduleName+"\"", ast.SymbolFlagsValueModule) + symbol := c.getSymbol(c.denoForkContext.CombinedGlobals(), "\""+moduleName+"\"", ast.SymbolFlagsValueModule) // merged symbol is module declaration symbol combined with all augmentations if withAugmentations { return c.getMergedSymbol(symbol) @@ -14850,13 +14929,28 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool return symbol } -func (c *Checker) GetAmbientModules() []*ast.Symbol { - c.ambientModulesOnce.Do(func() { - for sym, global := range c.globals.Iter() { if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { - c.ambientModules = append(c.ambientModules, global) - } } - }) - return c.ambientModules +func (c *Checker) GetAmbientModules(sourceFile *ast.SourceFile) []*ast.Symbol { + isNode := c.denoForkContext.HasNodeSourceFile(&sourceFile.Node) + if isNode { + c.nodeAmbientModulesOnce.Do(func() { + for sym, global := range c.nodeGlobals.Iter() { + if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { + c.ambientModules = append(c.ambientModules, global) + } + } + }) + return c.nodeAmbientModules + } else { + + c.ambientModulesOnce.Do(func() { + for sym, global := range c.denoGlobals.Iter() { + if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { + c.ambientModules = append(c.ambientModules, global) + } + } + }) + return c.ambientModules + } } func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol { @@ -15412,7 +15506,9 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as // Add non-type-only names 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.Iter() { nonTypeOnlyNames.Add(name) } + for name := range symbol.Exports.Iter() { + nonTypeOnlyNames.Add(name) + } } if symbol == nil || symbol.Exports == nil || slices.Contains(visitedSymbols, symbol) { return nil @@ -15444,7 +15540,9 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as if typeOnlyExportStarMap == nil { typeOnlyExportStarMap = make(map[string]*ast.Node) } - for name := range symbols.Iter() { typeOnlyExportStarMap[name] = exportStar } + for name := range symbols.Iter() { + typeOnlyExportStarMap[name] = exportStar + } } return symbols } @@ -15465,11 +15563,12 @@ 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.Iter() { if id == ast.InternalSymbolNameDefault { + for id, sourceSymbol := range source.Iter() { + if id == ast.InternalSymbolNameDefault { continue } -targetSymbol := target.Get(id) -if targetSymbol == nil { + targetSymbol := target.Get(id) + if targetSymbol == nil { target.Set(id, sourceSymbol) if lookupTable != nil && exportNode != nil { lookupTable[id] = &ExportCollision{ @@ -15479,7 +15578,8 @@ if targetSymbol == nil { } else if lookupTable != nil && exportNode != nil && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { s := lookupTable[id] s.exportsWithDuplicate = append(s.exportsWithDuplicate, exportNode) - } } + } + } } func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) { @@ -19777,11 +19877,13 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { // Combinations of function, class, enum and module members := c.getExportsOfSymbol(symbol) var indexInfos []*IndexInfo - if symbol == c.globalThisSymbol { + if symbol == c.denoGlobalThisSymbol || symbol == c.nodeGlobalThisSymbol { varsOnly := ast.NewSymbolTable() - for _, p := range members.Iter() { if p.Flags&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags&ast.SymbolFlagsValueModule != 0 && len(p.Declarations) != 0 && core.Every(p.Declarations, ast.IsAmbientModule)) { + for _, p := range members.Iter() { + if p.Flags&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags&ast.SymbolFlagsValueModule != 0 && len(p.Declarations) != 0 && core.Every(p.Declarations, ast.IsAmbientModule)) { varsOnly.Set(p.Name, p) - } } + } + } members = varsOnly } var baseConstructorIndexInfo *IndexInfo @@ -19850,13 +19952,15 @@ func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper, return nil } result := ast.NewSymbolTableWithCapacity(symbols.Len()) - for id, symbol := range symbols.Iter() { if c.isNamedMember(symbol, id) { + for id, symbol := range symbols.Iter() { + if c.isNamedMember(symbol, id) { if mappingThisOnly && isThisless(symbol) { result.Set(id, symbol) } else { result.Set(id, c.instantiateSymbol(symbol, m)) } - } } + } + } return result } @@ -21186,13 +21290,15 @@ func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { } func (c *Checker) getNamedMembers(members ast.SymbolTable) []*ast.Symbol { - if members.Len() == 0 { + if members == nil || members.Len() == 0 { return nil } result := make([]*ast.Symbol, 0, members.Len()) - for id, symbol := range members.Iter() { if c.isNamedMember(symbol, id) { + for id, symbol := range members.Iter() { + if c.isNamedMember(symbol, id) { result = append(result, symbol) - } } + } + } c.sortSymbols(result) return result } @@ -22847,9 +22953,11 @@ 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().Iter() { if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { + for _, symbol := range node.Locals().Iter() { + if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { result = append(result, c.getDeclaredTypeOfSymbol(symbol)) - } } + } + } return result } @@ -26116,7 +26224,9 @@ func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectTy return c.getUnionType(append(types, c.undefinedType)) } } - if objectType.symbol == c.globalThisSymbol && hasPropName && c.globalThisSymbol.Exports.Get(propName) != nil && c.globalThisSymbol.Exports.Get(propName).Flags&ast.SymbolFlagsBlockScoped != 0 { + if objectType.symbol == c.denoGlobalThisSymbol && hasPropName && c.denoGlobalThisSymbol.Exports.Get(propName) != nil && c.denoGlobalThisSymbol.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 objectType.symbol == c.nodeGlobalThisSymbol && hasPropName && c.nodeGlobalThisSymbol.Exports.Get(propName) != nil && c.nodeGlobalThisSymbol.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) { @@ -27381,7 +27491,7 @@ func (c *Checker) markExportSpecifierAliasReferenced(location *ast.ExportSpecifi return // Skip for invalid syntax like this: export { "x" } } symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { + if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.denoGlobalThisSymbol || symbol == c.nodeGlobalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { // Do nothing, non-local symbol } else { target := symbol diff --git a/internal/checker/services.go b/internal/checker/services.go index c0f8573949..80a3c1baa2 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -104,7 +104,10 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) location = location.Parent } - copySymbols(c.globals, meaning) + if c.denoForkContext.HasNodeSourceFile(location) { + copySymbols(c.nodeGlobals, meaning) + } + copySymbols(c.denoGlobals, meaning) } populateSymbols() @@ -836,4 +839,3 @@ func isArrayLiteralOrObjectLiteralDestructuringPattern(node *ast.Node) bool { // [x, [a, b, c] ] = someExpression return isArrayLiteralOrObjectLiteralDestructuringPattern(parent) } - diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index 445e1c0b83..92532474aa 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -507,8 +507,11 @@ func (ch *Checker) trySymbolTable( } // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that - if reflect.ValueOf(ch.globals).UnsafePointer() == reflect.ValueOf(symbols).UnsafePointer() { - return ch.getCandidateListForSymbol(ctx, ch.globalThisSymbol, ch.globalThisSymbol, ignoreQualification) + if reflect.ValueOf(ch.denoGlobals).UnsafePointer() == reflect.ValueOf(symbols).UnsafePointer() { + return ch.getCandidateListForSymbol(ctx, ch.denoGlobalThisSymbol, ch.denoGlobalThisSymbol, ignoreQualification) + } + if reflect.ValueOf(ch.nodeGlobals).UnsafePointer() == reflect.ValueOf(symbols).UnsafePointer() { + return ch.getCandidateListForSymbol(ctx, ch.nodeGlobalThisSymbol, ch.nodeGlobalThisSymbol, ignoreQualification) } return nil } @@ -708,7 +711,7 @@ func (ch *Checker) someSymbolTableInScope( } } - return callback(ch.globals, false, true, nil) + return callback(ch.denoGlobals, false, true, nil) } /** diff --git a/internal/execute/incremental/programtosnapshot.go b/internal/execute/incremental/programtosnapshot.go index 69f8cd5a7e..e6abe07657 100644 --- a/internal/execute/incremental/programtosnapshot.go +++ b/internal/execute/incremental/programtosnapshot.go @@ -294,7 +294,7 @@ func getReferencedFiles(program *compiler.Program, file *ast.SourceFile) *collec } // From ambient modules - for _, ambientModule := range checker.GetAmbientModules() { + for _, ambientModule := range checker.GetAmbientModules(file) { addReferencedFilesFromSymbol(file, &referencedFiles, ambientModule) } return core.IfElse(referencedFiles.Len() > 0, &referencedFiles, nil) diff --git a/internal/ls/api.go b/internal/ls/api.go index d198f12127..914fe4379c 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -45,16 +45,15 @@ func (l *LanguageService) GetTypeOfSymbol(ctx context.Context, symbol *ast.Symbo return checker.GetTypeOfSymbolAtLocation(symbol, nil) } - type Position struct { - Line int64 `json:"line"` + Line int64 `json:"line"` Character int64 `json:"character"` } func getPosition(file *ast.SourceFile, position int, ls *LanguageService) Position { pos := ls.createLspPosition(position, file) return Position{ - Line: int64(pos.Line), + Line: int64(pos.Line), Character: int64(pos.Character), } } @@ -66,8 +65,8 @@ type Diagnostic struct { FileName string `json:"fileName"` Start Position `json:"start"` End Position `json:"end"` - StartPos int `json:"startPos"` - EndPos int `json:"endPos"` + StartPos int `json:"startPos"` + EndPos int `json:"endPos"` Code int32 `json:"code"` Category string `json:"category"` Message string `json:"message"` diff --git a/internal/ls/autoimports.go b/internal/ls/autoimports.go index 3b38e0d637..7db2ec168e 100644 --- a/internal/ls/autoimports.go +++ b/internal/ls/autoimports.go @@ -1370,7 +1370,7 @@ func forEachExternalModule( // !!! excludePatterns // isExcluded := excludePatterns && getIsExcluded(excludePatterns, host) - for _, ambient := range ch.GetAmbientModules() { + for _, ambient := range ch.GetAmbientModules(nil) { if !strings.Contains(ambient.Name, "*") /* && !(excludePatterns && ambient.Declarations.every(func (d){ return isExcluded(d.getSourceFile())})) */ { cb(ambient, nil /*sourceFile*/) } diff --git a/internal/module/types.go b/internal/module/types.go index df3dd0e13f..d36e013beb 100644 --- a/internal/module/types.go +++ b/internal/module/types.go @@ -60,20 +60,20 @@ func (p *PackageId) PackageName() string { } type LookupLocations struct { - FailedLookupLocations []string `json:"failedLookupLocations"` - AffectingLocations []string `json:"affectingLocations"` + FailedLookupLocations []string `json:"failedLookupLocations"` + AffectingLocations []string `json:"affectingLocations"` ResolutionDiagnostics []*ast.Diagnostic `json:"resolutionDiagnostics"` } type ResolvedModule struct { LookupLocations - ResolvedFileName string `json:"resolvedFileName"` - OriginalPath string `json:"originalPath"` - Extension string `json:"extension"` - ResolvedUsingTsExtension bool `json:"resolvedUsingTsExtension"` + ResolvedFileName string `json:"resolvedFileName"` + OriginalPath string `json:"originalPath"` + Extension string `json:"extension"` + ResolvedUsingTsExtension bool `json:"resolvedUsingTsExtension"` PackageId PackageId `json:"packageId"` - IsExternalLibraryImport bool `json:"isExternalLibraryImport"` - AlternateResult string `json:"alternateResult"` + IsExternalLibraryImport bool `json:"isExternalLibraryImport"` + AlternateResult string `json:"alternateResult"` } func (r *ResolvedModule) IsResolved() bool { @@ -86,11 +86,11 @@ func (r *ResolvedModule) GetLookupLocations() *LookupLocations { type ResolvedTypeReferenceDirective struct { LookupLocations - Primary bool `json:"primary"` - ResolvedFileName string `json:"resolvedFileName"` - OriginalPath string `json:"originalPath"` + Primary bool `json:"primary"` + ResolvedFileName string `json:"resolvedFileName"` + OriginalPath string `json:"originalPath"` PackageId PackageId `json:"packageId"` - IsExternalLibraryImport bool `json:"isExternalLibraryImport"` + IsExternalLibraryImport bool `json:"isExternalLibraryImport"` } func (r *ResolvedTypeReferenceDirective) IsResolved() bool { diff --git a/internal/tsoptions/enummaps.go b/internal/tsoptions/enummaps.go index 798a8c62f1..00f932bbcd 100644 --- a/internal/tsoptions/enummaps.go +++ b/internal/tsoptions/enummaps.go @@ -113,7 +113,7 @@ var LibMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, an {Key: "deno.window", Value: "lib.deno.window.d.ts"}, {Key: "deno.worker", Value: "lib.deno.worker.d.ts"}, {Key: "deno.ns", Value: "lib.deno.ns.d.ts"}, - + {Key: "deno.console", Value: "lib.deno.console.d.ts"}, {Key: "deno.url", Value: "lib.deno.url.d.ts"}, {Key: "deno.web", Value: "lib.deno.web.d.ts"}, From a54d5e9d45caad10807fb21273dae21112520c44 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 29 Sep 2025 11:25:23 -0700 Subject: [PATCH 14/28] debugging --- internal/api/api.go | 23 +++++++++++++++++++++-- internal/ast/ast.go | 3 +++ internal/checker/checker.go | 24 ++++++++++++++++++++---- internal/ls/api.go | 14 +++++++------- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 4f6dfc320a..86529285d9 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "os" + "runtime" "sync" "github.com/go-json-experiment/json" @@ -62,6 +64,10 @@ func (api *API) HandleRequest(ctx context.Context, method string, payload []byte if err != nil { return nil, err } + fmt.Fprintf(os.Stderr, "method: %s, params: %v\n", method, params) + defer func() { + fmt.Fprintf(os.Stderr, "done handling method: %s, params: %v\n", method, params) + }() switch Method(method) { case MethodRelease: @@ -265,7 +271,7 @@ func (api *API) GetSourceFile(projectId Handle[project.Project], fileName string return sourceFile, nil } -func (api *API) GetDiagnostics(ctx context.Context, projectId Handle[project.Project]) ([]*ls.Diagnostic, error) { +func (api *API) GetDiagnostics(ctx context.Context, projectId Handle[project.Project]) ([]ls.Diagnostic, error) { projectPath, ok := api.projects[projectId] if !ok { return nil, errors.New("project ID not found") @@ -279,6 +285,7 @@ func (api *API) GetDiagnostics(ctx context.Context, projectId Handle[project.Pro languageService := ls.NewLanguageService(project, snapshot.Converters()) diagnostics := languageService.GetDiagnostics(ctx) + fmt.Fprintf(os.Stderr, "diagnostics: %v\n", diagnostics) api.symbolsMu.Lock() defer api.symbolsMu.Unlock() @@ -335,9 +342,21 @@ func (api *API) toPath(fileName string) tspath.Path { return tspath.ToPath(fileName, api.session.GetCurrentDirectory(), api.session.FS().UseCaseSensitiveFileNames()) } +func stackTrace() string { + buf := make([]byte, 1024) + n := runtime.Stack(buf, false) + return string(buf[:n]) +} + func encodeJSON(v any, err error) ([]byte, error) { if err != nil { + fmt.Fprintf(os.Stderr, "encodeJSON: %v\n", err) + return nil, err + } + b, err := json.Marshal(v) + if err != nil { + fmt.Fprintf(os.Stderr, "encodeJSON err: %v\n, v: %v\n; stackTrace: %v\n", err, v, stackTrace()) return nil, err } - return json.Marshal(v) + return b, nil } diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 8aed54be47..3367a2085f 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -290,6 +290,9 @@ func (n *Node) LocalSymbol() *Symbol { func (n *Node) Locals() SymbolTable { data := n.LocalsContainerData() if data != nil { + if data.Locals == nil { + data.Locals = NewSymbolTable() + } return data.Locals } return nil diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 64ba429f9b..136b031dc0 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -3631,7 +3631,7 @@ func (c *Checker) checkBlock(node *ast.Node) { } else { c.checkSourceElements(node.Statements()) } - if node.Locals().Len() != 0 { + if node.Locals() != nil && node.Locals().Len() != 0 { c.registerForUnusedIdentifiersCheck(node) } } @@ -15695,7 +15695,7 @@ func (c *Checker) getSymbolFlagsEx(symbol *ast.Symbol, excludeTypeOnlyMeanings b typeOnlyResolution = c.resolveAlias(typeOnlyDeclaration.Symbol()) } } - var typeOnlyExportStarTargets ast.SymbolTable + var typeOnlyExportStarTargets ast.SymbolTable = ast.NewSymbolTable() if typeOnlyDeclarationIsExportStar && typeOnlyResolution != nil { typeOnlyExportStarTargets = c.getExportsOfModule(typeOnlyResolution) } @@ -18307,7 +18307,11 @@ func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { panic("Unhandled case in resolveStructuredTypeMembers") } } - return t.AsStructuredType() + ret := t.AsStructuredType() + if ret.members == nil { + ret.members = ast.NewSymbolTable() + } + return ret } func (c *Checker) resolveClassOrInterfaceMembers(t *Type) { @@ -18327,7 +18331,7 @@ func (c *Checker) resolveTypeReferenceMembers(t *Type) { func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters []*Type, typeArguments []*Type) { var mapper *TypeMapper - var members ast.SymbolTable + members := ast.NewSymbolTable() var callSignatures []*Signature var constructSignatures []*Signature var indexInfos []*IndexInfo @@ -18335,6 +18339,9 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters resolved := c.resolveDeclaredMembers(source) if slices.Equal(typeParameters, typeArguments) { members = resolved.declaredMembers + if members == nil { + panic("members is nil in resolveObjectTypeMembers 1") + } callSignatures = resolved.declaredCallSignatures constructSignatures = resolved.declaredConstructSignatures indexInfos = resolved.declaredIndexInfos @@ -18342,6 +18349,9 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters instantiated = true mapper = newTypeMapper(typeParameters, typeArguments) members = c.instantiateSymbolTable(resolved.declaredMembers, mapper, len(typeParameters) == 1 /*mappingThisOnly*/) + if members == nil { + members = ast.NewSymbolTable() + } callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, mapper) constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, mapper) indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, mapper) @@ -18350,6 +18360,9 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters if len(baseTypes) != 0 { if !instantiated { members = members.Clone() + if members == nil { + panic("members is nil in resolveObjectTypeMembers 2") + } } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) thisArgument := core.LastOrNil(typeArguments) @@ -19961,6 +19974,9 @@ func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper, } } } + if result == nil { + panic("result is nil in instantiateSymbolTable") + } return result } diff --git a/internal/ls/api.go b/internal/ls/api.go index 914fe4379c..a65ad1d707 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -78,7 +78,7 @@ type Diagnostic struct { } type diagnosticMaps struct { - diagnosticMapById map[DiagnosticId]*Diagnostic + diagnosticMapById map[DiagnosticId]Diagnostic diagnosticReverseMap map[*ast.Diagnostic]DiagnosticId } @@ -88,7 +88,7 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageS } id := DiagnosticId(len(d.diagnosticMapById) + 1) - diag := &Diagnostic{ + diag := Diagnostic{ Id: id, FileName: diagnostic.File().FileName(), Start: getPosition(diagnostic.File(), diagnostic.Loc().Pos(), ls), @@ -116,23 +116,23 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageS return id } -func (d *diagnosticMaps) getDiagnostics() []*Diagnostic { - diagnostics := make([]*Diagnostic, 0, len(d.diagnosticMapById)) +func (d *diagnosticMaps) getDiagnostics() []Diagnostic { + diagnostics := make([]Diagnostic, 0, len(d.diagnosticMapById)) for _, diagnostic := range d.diagnosticMapById { diagnostics = append(diagnostics, diagnostic) } - slices.SortFunc(diagnostics, func(a, b *Diagnostic) int { + slices.SortFunc(diagnostics, func(a, b Diagnostic) int { return int(int64(a.Id) - int64(b.Id)) }) return diagnostics } -func (l *LanguageService) GetDiagnostics(ctx context.Context) []*Diagnostic { +func (l *LanguageService) GetDiagnostics(ctx context.Context) []Diagnostic { program := l.GetProgram() sourceFiles := program.GetSourceFiles() diagnosticMaps := &diagnosticMaps{ - diagnosticMapById: make(map[DiagnosticId]*Diagnostic), + diagnosticMapById: make(map[DiagnosticId]Diagnostic), diagnosticReverseMap: make(map[*ast.Diagnostic]DiagnosticId), } diagnostics := make([]*ast.Diagnostic, 0, len(sourceFiles)) From a11c0f75faa6a27e781ea01db4cf3fcf9a4ec8cf Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 29 Sep 2025 14:42:49 -0700 Subject: [PATCH 15/28] callback for isNodesourceFile --- internal/api/server.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/internal/api/server.go b/internal/api/server.go index 4b024bf689..5aea5434e1 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "io" + "os" "runtime/debug" "strconv" "strings" @@ -70,6 +71,7 @@ const ( CallbackGetPackageJsonScopeIfApplicable CallbackGetPackageScopeForPath CallbackGetImpliedNodeFormatForFile + CallbackIsNodeSourceFile ) type ServerOptions struct { @@ -173,6 +175,26 @@ func (h *hostWrapper) SessionOptions() *project.SessionOptions { // IsNodeSourceFile implements project.ProjectHost. func (h *hostWrapper) IsNodeSourceFile(path tspath.Path) bool { + fmt.Fprintf(os.Stderr, "host IsNodeSourceFile %s\n", path) + if h.server.CallbackEnabled(CallbackIsNodeSourceFile) { + fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback %s\n", path) + result, err := h.server.call("isNodeSourceFile", path) + if err != nil { + panic(err) + } + if len(result) > 0 { + var res bool + if err := json.Unmarshal(result, &res); err != nil { + panic(err) + } + if res { + fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback returning true %s\n", path) + } else { + fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback returning false %s\n", path) + } + return res + } + } return h.inner.IsNodeSourceFile(path) } @@ -506,6 +528,8 @@ func (s *Server) enableCallback(callback string) error { s.enabledCallbacks |= CallbackGetPackageScopeForPath case "getImpliedNodeFormatForFile": s.enabledCallbacks |= CallbackGetImpliedNodeFormatForFile + case "isNodeSourceFile": + s.enabledCallbacks |= CallbackIsNodeSourceFile default: return fmt.Errorf("unknown callback: %s", callback) } From d40d5e157a72d5ef90baca5bd207a9524bba9320 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 29 Sep 2025 14:42:49 -0700 Subject: [PATCH 16/28] merge symbol fix --- internal/ast/symbol.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/internal/ast/symbol.go b/internal/ast/symbol.go index 8c415a01b9..9391d27c66 100644 --- a/internal/ast/symbol.go +++ b/internal/ast/symbol.go @@ -1,8 +1,10 @@ package ast import ( + "fmt" "iter" "maps" + "os" "strings" "sync/atomic" @@ -394,6 +396,7 @@ func (c *DenoForkContext) GetGlobalsForName(name string) SymbolTable { } func isTypesNodePkgPath(path tspath.Path) bool { + fmt.Fprintf(os.Stderr, "isTypesNodePkgPath %s\n", path) return strings.HasSuffix(string(path), ".d.ts") && strings.Contains(string(path), "/@types/node/") } @@ -411,6 +414,14 @@ func symbolHasAnyTypesNodePkgDecl(symbol *Symbol, hasNodeSourceFile func(*Node) } func (c *DenoForkContext) MergeGlobalSymbolTable(node *Node, source SymbolTable, unidirectional bool) { + name := "" + if node != nil { + decl := node.Name() + if decl != nil { + name = decl.Text() + } + } + fmt.Fprintf(os.Stderr, "MergeGlobalSymbolTable %s\n", name) sourceFile := GetSourceFileOfNode(node) isNodeFile := c.HasNodeSourceFile(node) isTypesNodeSourceFile := isNodeFile && isTypesNodePkgPath(sourceFile.Path()) @@ -423,10 +434,18 @@ func (c *DenoForkContext) MergeGlobalSymbolTable(node *Node, source SymbolTable, target = c.globals } targetSymbol := target.Get(id) + if isTypesNodeSourceFile { + fmt.Fprintf(os.Stderr, "isTypesNodeSourceFile %s\n", id) + } if isTypesNodeSourceFile && targetSymbol != nil && TypesNodeIgnorableNames.Has(id) && !symbolHasAnyTypesNodePkgDecl(targetSymbol, c.HasNodeSourceFile) { + fmt.Fprintf(os.Stderr, "ignoring %s\n", id) continue } - target.Set(id, sourceSymbol) + sym := sourceSymbol + if targetSymbol != nil { + sym = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) + } + target.Set(id, sym) } } From baeee73f93a28c1f22ea0ab5124cf92e16b711d0 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 29 Sep 2025 15:46:51 -0700 Subject: [PATCH 17/28] fixes --- internal/api/api.go | 15 ------- internal/api/server.go | 31 ++++++++------ internal/ast/symbol.go | 75 +++++++++++++++++++-------------- internal/binder/nameresolver.go | 3 +- internal/checker/checker.go | 21 +++++---- 5 files changed, 76 insertions(+), 69 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 86529285d9..6e31d3bd78 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -4,8 +4,6 @@ import ( "context" "errors" "fmt" - "os" - "runtime" "sync" "github.com/go-json-experiment/json" @@ -64,10 +62,6 @@ func (api *API) HandleRequest(ctx context.Context, method string, payload []byte if err != nil { return nil, err } - fmt.Fprintf(os.Stderr, "method: %s, params: %v\n", method, params) - defer func() { - fmt.Fprintf(os.Stderr, "done handling method: %s, params: %v\n", method, params) - }() switch Method(method) { case MethodRelease: @@ -285,7 +279,6 @@ func (api *API) GetDiagnostics(ctx context.Context, projectId Handle[project.Pro languageService := ls.NewLanguageService(project, snapshot.Converters()) diagnostics := languageService.GetDiagnostics(ctx) - fmt.Fprintf(os.Stderr, "diagnostics: %v\n", diagnostics) api.symbolsMu.Lock() defer api.symbolsMu.Unlock() @@ -342,20 +335,12 @@ func (api *API) toPath(fileName string) tspath.Path { return tspath.ToPath(fileName, api.session.GetCurrentDirectory(), api.session.FS().UseCaseSensitiveFileNames()) } -func stackTrace() string { - buf := make([]byte, 1024) - n := runtime.Stack(buf, false) - return string(buf[:n]) -} - func encodeJSON(v any, err error) ([]byte, error) { if err != nil { - fmt.Fprintf(os.Stderr, "encodeJSON: %v\n", err) return nil, err } b, err := json.Marshal(v) if err != nil { - fmt.Fprintf(os.Stderr, "encodeJSON err: %v\n, v: %v\n; stackTrace: %v\n", err, v, stackTrace()) return nil, err } return b, nil diff --git a/internal/api/server.go b/internal/api/server.go index 5aea5434e1..55455ab18c 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -6,7 +6,6 @@ import ( "encoding/binary" "fmt" "io" - "os" "runtime/debug" "strconv" "strings" @@ -175,9 +174,7 @@ func (h *hostWrapper) SessionOptions() *project.SessionOptions { // IsNodeSourceFile implements project.ProjectHost. func (h *hostWrapper) IsNodeSourceFile(path tspath.Path) bool { - fmt.Fprintf(os.Stderr, "host IsNodeSourceFile %s\n", path) if h.server.CallbackEnabled(CallbackIsNodeSourceFile) { - fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback %s\n", path) result, err := h.server.call("isNodeSourceFile", path) if err != nil { panic(err) @@ -187,11 +184,6 @@ func (h *hostWrapper) IsNodeSourceFile(path tspath.Path) bool { if err := json.Unmarshal(result, &res); err != nil { panic(err) } - if res { - fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback returning true %s\n", path) - } else { - fmt.Fprintf(os.Stderr, "IsNodeSourceFile callback returning false %s\n", path) - } return res } } @@ -219,9 +211,9 @@ func newResolverWrapper(inner module.ResolverInterface, server *Server) *resolve } type PackageJsonIfApplicable struct { - PackageDirectory string - DirectoryExists bool - Contents string + PackageDirectory string `json:"packageDirectory"` + DirectoryExists bool `json:"directoryExists"` + Contents string `json:"contents"` } // GetPackageJsonScopeIfApplicable implements module.ResolverInterface. @@ -265,11 +257,24 @@ func (r *resolverWrapper) GetPackageScopeForPath(directory string) *packagejson. panic(err) } if len(result) > 0 { - var res packagejson.InfoCacheEntry + var res *PackageJsonIfApplicable if err := json.Unmarshal(result, &res); err != nil { panic(err) } - return &res + if res == nil { + return nil + } + contents, err := packagejson.Parse([]byte(res.Contents)) + if err != nil { + panic(err) + } + return &packagejson.InfoCacheEntry{ + PackageDirectory: res.PackageDirectory, + DirectoryExists: res.DirectoryExists, + Contents: &packagejson.PackageJson{ + Fields: contents, + }, + } } } return r.inner.GetPackageScopeForPath(directory) diff --git a/internal/ast/symbol.go b/internal/ast/symbol.go index 9391d27c66..8d0d9a4d20 100644 --- a/internal/ast/symbol.go +++ b/internal/ast/symbol.go @@ -1,10 +1,8 @@ package ast import ( - "fmt" "iter" "maps" - "os" "strings" "sync/atomic" @@ -199,16 +197,18 @@ func (c *CombinedSymbolTable) Each(fn func(name string, symbol *Symbol)) { // Find implements SymbolTable. func (c *CombinedSymbolTable) Find(predicate func(*Symbol) bool) *Symbol { - if c.firstTable.Find(predicate) != nil { - return c.firstTable.Find(predicate) + ret := c.firstTable.Find(predicate) + if ret != nil { + return ret } return c.secondTable.Find(predicate) } // Get implements SymbolTable. func (c *CombinedSymbolTable) Get(name string) *Symbol { - if c.firstTable.Get(name) != nil { - return c.firstTable.Get(name) + ret := c.firstTable.Get(name) + if ret != nil { + return ret } return c.secondTable.Get(name) } @@ -225,29 +225,46 @@ func (c *CombinedSymbolTable) Get2(name string) (*Symbol, bool) { func (c *CombinedSymbolTable) Iter() iter.Seq2[string, *Symbol] { seen := make(map[string]struct{}) return func(yield func(string, *Symbol) bool) { - c.firstTable.Iter()(func(name string, symbol *Symbol) bool { + for name, symbol := range c.firstTable.Iter() { if _, ok := seen[name]; !ok { seen[name] = struct{}{} - return yield(name, symbol) + if !yield(name, symbol) { + break + } } - return true - }) - c.secondTable.Iter()(func(name string, symbol *Symbol) bool { + } + for name, symbol := range c.secondTable.Iter() { if _, ok := seen[name]; !ok { seen[name] = struct{}{} - return yield(name, symbol) + if !yield(name, symbol) { + return + } } - return true - }) + } } } // Keys implements SymbolTable. func (c *CombinedSymbolTable) Keys() iter.Seq[string] { return func(yield func(string) bool) { - c.Iter()(func(name string, symbol *Symbol) bool { - return yield(name) - }) + seen := make(map[string]struct{}) + for name := range c.firstTable.Keys() { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + if !yield(name) { + break + } + } + } + + for name := range c.secondTable.Keys() { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + if !yield(name) { + return + } + } + } } } @@ -336,6 +353,7 @@ var TypesNodeIgnorableNames = collections.NewSetFromItems( "PerformanceEntry", "PerformanceMark", "PerformanceMeasure", + "QueuingStrategy", "ReadableByteStreamController", "ReadableStream", "ReadableStreamBYOBReader", @@ -366,6 +384,7 @@ type DenoForkContext struct { nodeGlobals SymbolTable combinedGlobals SymbolTable mergeSymbol func(target *Symbol, source *Symbol, unidirectional bool) *Symbol + getMergedSymbol func(source *Symbol) *Symbol isNodeSourceFile func(path tspath.Path) bool } @@ -373,6 +392,7 @@ func NewDenoForkContext( globals SymbolTable, nodeGlobals SymbolTable, mergeSymbol func(target *Symbol, source *Symbol, unidirectional bool) *Symbol, + getMergedSymbol func(source *Symbol) *Symbol, isNodeSourceFile func(path tspath.Path) bool, ) *DenoForkContext { return &DenoForkContext{ @@ -383,6 +403,7 @@ func NewDenoForkContext( secondTable: globals, }, mergeSymbol: mergeSymbol, + getMergedSymbol: getMergedSymbol, isNodeSourceFile: isNodeSourceFile, } } @@ -396,7 +417,6 @@ func (c *DenoForkContext) GetGlobalsForName(name string) SymbolTable { } func isTypesNodePkgPath(path tspath.Path) bool { - fmt.Fprintf(os.Stderr, "isTypesNodePkgPath %s\n", path) return strings.HasSuffix(string(path), ".d.ts") && strings.Contains(string(path), "/@types/node/") } @@ -414,14 +434,6 @@ func symbolHasAnyTypesNodePkgDecl(symbol *Symbol, hasNodeSourceFile func(*Node) } func (c *DenoForkContext) MergeGlobalSymbolTable(node *Node, source SymbolTable, unidirectional bool) { - name := "" - if node != nil { - decl := node.Name() - if decl != nil { - name = decl.Text() - } - } - fmt.Fprintf(os.Stderr, "MergeGlobalSymbolTable %s\n", name) sourceFile := GetSourceFileOfNode(node) isNodeFile := c.HasNodeSourceFile(node) isTypesNodeSourceFile := isNodeFile && isTypesNodePkgPath(sourceFile.Path()) @@ -435,17 +447,18 @@ func (c *DenoForkContext) MergeGlobalSymbolTable(node *Node, source SymbolTable, } targetSymbol := target.Get(id) if isTypesNodeSourceFile { - fmt.Fprintf(os.Stderr, "isTypesNodeSourceFile %s\n", id) } if isTypesNodeSourceFile && targetSymbol != nil && TypesNodeIgnorableNames.Has(id) && !symbolHasAnyTypesNodePkgDecl(targetSymbol, c.HasNodeSourceFile) { - fmt.Fprintf(os.Stderr, "ignoring %s\n", id) continue } - sym := sourceSymbol + var merged *Symbol if targetSymbol != nil { - sym = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) + merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) + } else { + merged = c.getMergedSymbol(sourceSymbol) } - target.Set(id, sym) + + target.Set(id, merged) } } diff --git a/internal/binder/nameresolver.go b/internal/binder/nameresolver.go index aeb206b6dd..8e73f66f14 100644 --- a/internal/binder/nameresolver.go +++ b/internal/binder/nameresolver.go @@ -322,7 +322,8 @@ loop: if !excludeGlobals { if r.DenoContext.HasNodeSourceFile(lastLocation) { result = r.lookup(r.NodeGlobals, name, meaning|ast.SymbolFlagsGlobalLookup) - } else { + } + if result == nil { result = r.lookup(r.DenoGlobals, name, meaning|ast.SymbolFlagsGlobalLookup) } } diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 136b031dc0..9590a13e75 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -928,11 +928,10 @@ func NewChecker(program Program) *Checker { c.denoGlobalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.denoGlobalThisSymbol.Exports = c.denoGlobals c.denoGlobals.Set(c.denoGlobalThisSymbol.Name, c.denoGlobalThisSymbol) - c.denoForkContext = ast.NewDenoForkContext(c.denoGlobals, c.nodeGlobals, c.mergeSymbol, c.program.IsNodeSourceFile) + c.denoForkContext = ast.NewDenoForkContext(c.denoGlobals, c.nodeGlobals, c.mergeSymbol, c.getMergedSymbol, c.program.IsNodeSourceFile) c.nodeGlobalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.nodeGlobalThisSymbol.Exports = c.denoForkContext.CombinedGlobals() c.nodeGlobals.Set(c.nodeGlobalThisSymbol.Name, c.nodeGlobalThisSymbol) - c.nodeGlobals.Set(c.nodeGlobalThisSymbol.Name, c.nodeGlobalThisSymbol) c.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve c.tupleTypes = make(map[string]*Type) @@ -1281,6 +1280,7 @@ func (c *Checker) initializeChecker() { for _, file := range c.files { if !ast.IsExternalOrCommonJSModule(file) { c.denoForkContext.MergeGlobalSymbolTable(&file.Node, file.Locals, false /*unidirectional*/) + // c.mergeSymbolTable(c.denoGlobals, file.Locals, false,) } c.patternAmbientModules = append(c.patternAmbientModules, file.PatternAmbientModules...) augmentations = append(augmentations, file.ModuleAugmentations) @@ -10871,7 +10871,9 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l } return c.anyType } + // deno: ensure condition matches above if leftType.symbol == c.nodeGlobalThisSymbol { + // deno: don't bother with errors like above for simplicity return c.anyType } if right.Text() != "" && !c.checkAndReportErrorForExtendingInterface(node) { @@ -14250,11 +14252,12 @@ func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symb // In Node.js, CommonJS modules always have a synthetic default when imported into ESM return true } - if usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindESNext { - // No matter what the `module` setting is, if we're confident that both files - // are ESM, there cannot be a synthetic default. - return false - } + // deno: commented out for https://github.com/microsoft/TypeScript/issues/51321 + // if usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindESNext { + // // No matter what the `module` setting is, if we're confident that both files + // // are ESM, there cannot be a synthetic default. + // return false + // } } if !c.allowSyntheticDefaultImports { return false @@ -14922,7 +14925,7 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool return nil } symbol := c.getSymbol(c.denoForkContext.CombinedGlobals(), "\""+moduleName+"\"", ast.SymbolFlagsValueModule) - // merged symbol is module declaration symbol combined with all augmentations + // module declaration symbol combined with all augmentations if withAugmentations { return c.getMergedSymbol(symbol) } @@ -14933,7 +14936,7 @@ func (c *Checker) GetAmbientModules(sourceFile *ast.SourceFile) []*ast.Symbol { isNode := c.denoForkContext.HasNodeSourceFile(&sourceFile.Node) if isNode { c.nodeAmbientModulesOnce.Do(func() { - for sym, global := range c.nodeGlobals.Iter() { + for sym, global := range c.denoForkContext.CombinedGlobals().Iter() { if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { c.ambientModules = append(c.ambientModules, global) } From 9fd0b89a6a4470d4fbcfa81e0b737cdaf4dec9b4 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 14:02:20 -0700 Subject: [PATCH 18/28] cleanup --- internal/checker/checker.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 9590a13e75..2147d61243 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -18342,9 +18342,6 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters resolved := c.resolveDeclaredMembers(source) if slices.Equal(typeParameters, typeArguments) { members = resolved.declaredMembers - if members == nil { - panic("members is nil in resolveObjectTypeMembers 1") - } callSignatures = resolved.declaredCallSignatures constructSignatures = resolved.declaredConstructSignatures indexInfos = resolved.declaredIndexInfos @@ -18363,9 +18360,6 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters if len(baseTypes) != 0 { if !instantiated { members = members.Clone() - if members == nil { - panic("members is nil in resolveObjectTypeMembers 2") - } } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) thisArgument := core.LastOrNil(typeArguments) From 452ce2e7c251594996b369324c0ebd1996702ab6 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 14:15:49 -0700 Subject: [PATCH 19/28] merge fixup --- internal/checker/checker.go | 4 ++-- internal/checker/emitresolver.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 4f2d4e6187..175b9235d5 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -6451,7 +6451,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)) } @@ -15550,7 +15550,7 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as for _, node := range exportStars.Declarations { resolvedModule := c.resolveExternalModuleName(node, node.AsExportDeclaration().ModuleSpecifier, false /*ignoreErrors*/) exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.AsExportDeclaration().IsTypeOnly) - c.extendExportSymbols(nestedSymbols, exportedSymbols, lookupTable, node) + c.extendExportSymbols(nestedSymbols, ast.GetSymbolTable(&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 diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 0fbbbdf6b5..0bc6f07d42 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -519,7 +519,8 @@ func (r *emitResolver) IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) r.checkerMu.Lock() defer r.checkerMu.Unlock() // Exactly `globalThis.Symbol.something` and `globalThis` resolves to the global `globalThis` - return r.checker.getResolvedSymbol(node.Expression().Expression()) == r.checker.globalThisSymbol + resolved := r.checker.getResolvedSymbol(node.Expression().Expression()) + return resolved == r.checker.denoGlobalThisSymbol || resolved == r.checker.nodeGlobalThisSymbol } func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { From 4201de853cef0dbb0575574f56f18e2d6b943f2f Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 16:24:39 -0700 Subject: [PATCH 20/28] release workflow --- .github/workflows/release.yml | 129 ++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..5c654c3df0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,129 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +defaults: + run: + shell: bash + +jobs: + build: + name: Build ${{ matrix.archive_suffix }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + archive_suffix: linux-x64 + binary_name: tsgo + - goos: linux + goarch: arm64 + archive_suffix: linux-arm64 + binary_name: tsgo + - goos: darwin + goarch: amd64 + archive_suffix: macos-x64 + binary_name: tsgo + - goos: darwin + goarch: arm64 + archive_suffix: macos-arm64 + binary_name: tsgo + - goos: windows + goarch: amd64 + archive_suffix: windows-x64 + binary_name: tsgo.exe + + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - uses: ./.github/actions/setup-go + + - name: Determine version + id: version + run: | + VERSION="${GITHUB_REF_NAME#v}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Build release archive + id: package + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + set -euo pipefail + + VERSION="${VERSION:?}" + TARGET="typescript-go-${VERSION}-${{ matrix.archive_suffix }}" + OUT_DIR="dist/${TARGET}" + BIN_NAME="${{ matrix.binary_name }}" + + mkdir -p "$OUT_DIR" + + go build \ + -trimpath \ + -tags "noembed,release" \ + -ldflags "-s -w -X github.com/microsoft/typescript-go/internal/core.version=${VERSION}" \ + -o "$OUT_DIR/$BIN_NAME" \ + ./cmd/tsgo + + cp internal/bundled/libs/* "$OUT_DIR/" + cp LICENSE "$OUT_DIR/" + cp NOTICE.txt "$OUT_DIR/" + + ARCHIVE_NAME="${TARGET}.zip" + mkdir -p artifacts + (cd dist && zip -9 -r "../artifacts/${ARCHIVE_NAME}" "${TARGET}") + + echo "archive-path=artifacts/${ARCHIVE_NAME}" >> "$GITHUB_OUTPUT" + + - name: Upload release artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: tsgo-${{ matrix.archive_suffix }}-${{ steps.version.outputs.version }} + path: ${{ steps.package.outputs.archive-path }} + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - name: Download build artifacts + uses: actions/download-artifact@5ada21cd7d6b9e753874d339dbf9b6d3f3c8ba4f # v4.1.8 + with: + path: release + merge-multiple: true + + - name: Create or update GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + RAW_TAG="${GITHUB_REF_NAME}" + VERSION="${RAW_TAG#v}" + TITLE="TypeScript Go ${VERSION}" + NOTES="Release ${VERSION}" + + shopt -s nullglob + ASSETS=(release/*.zip) + if [ ${#ASSETS[@]} -eq 0 ]; then + echo "No release assets found" >&2 + exit 1 + fi + + if gh release view "$RAW_TAG" >/dev/null 2>&1; then + gh release edit "$RAW_TAG" --title "$TITLE" --notes "$NOTES" + gh release upload "$RAW_TAG" "${ASSETS[@]}" --clobber + else + gh release create "$RAW_TAG" "${ASSETS[@]}" --title "$TITLE" --notes "$NOTES" --latest --verify-tag + fi From 88fb8d00c46a3977dae68c268dee309217a81bb4 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 16:30:20 -0700 Subject: [PATCH 21/28] fix workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c654c3df0..6048c655f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,7 +97,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download build artifacts - uses: actions/download-artifact@5ada21cd7d6b9e753874d339dbf9b6d3f3c8ba4f # v4.1.8 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: path: release merge-multiple: true From c94e8d9d0eff0b4a31e14bf5e136ad01938432cd Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 16:38:19 -0700 Subject: [PATCH 22/28] fix workflow --- .github/workflows/release.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6048c655f5..eeaf20e612 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,6 +113,7 @@ jobs: VERSION="${RAW_TAG#v}" TITLE="TypeScript Go ${VERSION}" NOTES="Release ${VERSION}" + REPO="${GITHUB_REPOSITORY}" shopt -s nullglob ASSETS=(release/*.zip) @@ -121,9 +122,9 @@ jobs: exit 1 fi - if gh release view "$RAW_TAG" >/dev/null 2>&1; then - gh release edit "$RAW_TAG" --title "$TITLE" --notes "$NOTES" - gh release upload "$RAW_TAG" "${ASSETS[@]}" --clobber + if gh release view "$RAW_TAG" --repo "$REPO" >/dev/null 2>&1; then + gh release edit "$RAW_TAG" --repo "$REPO" --title "$TITLE" --notes "$NOTES" + gh release upload "$RAW_TAG" "${ASSETS[@]}" --repo "$REPO" --clobber else - gh release create "$RAW_TAG" "${ASSETS[@]}" --title "$TITLE" --notes "$NOTES" --latest --verify-tag + gh release create "$RAW_TAG" "${ASSETS[@]}" --repo "$REPO" --title "$TITLE" --notes "$NOTES" --latest --verify-tag fi From 522aedd9082026b5dbb39c57f16602a98b0e7ed7 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 30 Sep 2025 16:49:39 -0700 Subject: [PATCH 23/28] pass source line in diagnostic --- cmd/tsgo/main.go | 7 +------ internal/ls/api.go | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index 5eabc96df9..15b124c620 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -2,8 +2,6 @@ package main import ( "os" - - "github.com/microsoft/typescript-go/internal/execute" ) func main() { @@ -14,12 +12,9 @@ func runMain() int { args := os.Args[1:] if len(args) > 0 { switch args[0] { - case "--lsp": - return runLSP(args[1:]) case "--api": return runAPI(args[1:]) } } - result := execute.CommandLine(newSystem(), args, nil) - return int(result.Status) + return 1 } diff --git a/internal/ls/api.go b/internal/ls/api.go index a65ad1d707..c6f8f1e683 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -75,6 +75,7 @@ type Diagnostic struct { ReportsUnnecessary bool `json:"reportsUnnecessary"` ReportsDeprecated bool `json:"reportsDeprecated"` SkippedOnNoEmit bool `json:"skippedOnNoEmit"` + SourceLine string `json:"sourceLine"` } type diagnosticMaps struct { @@ -88,13 +89,26 @@ func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageS } id := DiagnosticId(len(d.diagnosticMapById) + 1) + startPos := diagnostic.Loc().Pos() + startPosLineCol := getPosition(diagnostic.File(), startPos, ls) + lineMap := ls.converters.getLineMap(diagnostic.File().FileName()) + lineStartPos := lineMap.LineStarts[startPosLineCol.Line] + var lineEndPos int + if int(startPosLineCol.Line+1) >= len(lineMap.LineStarts) { + lineEndPos = len(diagnostic.File().Text()) + } else { + lineEndPos = int(lineMap.LineStarts[startPosLineCol.Line+1]) - 1 + } + sourceLine := diagnostic.File().Text()[lineStartPos:lineEndPos] + diag := Diagnostic{ Id: id, FileName: diagnostic.File().FileName(), - Start: getPosition(diagnostic.File(), diagnostic.Loc().Pos(), ls), + Start: startPosLineCol, End: getPosition(diagnostic.File(), diagnostic.Loc().End(), ls), - StartPos: diagnostic.Loc().Pos(), + StartPos: startPos, EndPos: diagnostic.Loc().End(), + SourceLine: sourceLine, Code: diagnostic.Code(), Category: diagnostic.Category().Name(), Message: diagnostic.Message(), From 1ea2d83ebab71c1ff91a1c04bb1dd8932ff284c1 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 1 Oct 2025 16:17:31 -0700 Subject: [PATCH 24/28] upload source to release --- .github/workflows/release.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eeaf20e612..9385cf6804 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,12 +96,37 @@ jobs: needs: build runs-on: ubuntu-latest steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Download build artifacts uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: path: release merge-multiple: true + - name: Create source archive + run: | + set -euo pipefail + + RAW_TAG="${GITHUB_REF_NAME}" + VERSION="${RAW_TAG#v}" + ARCHIVE_FOLDER="typescript-go-${VERSION}" + ARCHIVE_NAME="${ARCHIVE_FOLDER}-source.zip" + TMP_DIR="$(mktemp -d)" + SOURCE_DIR="${TMP_DIR}/${ARCHIVE_FOLDER}" + + mkdir -p "${SOURCE_DIR}" + + rsync -a \ + --exclude='.git/' \ + --exclude='release/' \ + --exclude='node_modules/' \ + --exclude='testdata/' \ + ./ "${SOURCE_DIR}/" + + mkdir -p release + (cd "${TMP_DIR}" && zip -9 -r "${GITHUB_WORKSPACE}/release/${ARCHIVE_NAME}" "${ARCHIVE_FOLDER}") + - name: Create or update GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 21cddec5788104cdfbfb8fe84b782513ed7e7888 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 2 Oct 2025 10:33:08 -0700 Subject: [PATCH 25/28] always treat case sensitive --- internal/api/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/server.go b/internal/api/server.go index 55455ab18c..4e69f462d2 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -750,7 +750,7 @@ func (s *Server) Realpath(path string) string { // UseCaseSensitiveFileNames implements vfs.FS. func (s *Server) UseCaseSensitiveFileNames() bool { - return s.fs.UseCaseSensitiveFileNames() + return true } // WriteFile implements vfs.FS. From d97e1b9083f1aabef562f8d83324f85f49f3a622 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 2 Oct 2025 13:47:00 -0700 Subject: [PATCH 26/28] do not include lib files --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9385cf6804..c8fd6ca173 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,7 +76,6 @@ jobs: -o "$OUT_DIR/$BIN_NAME" \ ./cmd/tsgo - cp internal/bundled/libs/* "$OUT_DIR/" cp LICENSE "$OUT_DIR/" cp NOTICE.txt "$OUT_DIR/" From 225c76642d120e532d6ed0e7f0ffb70162bcd594 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 2 Oct 2025 17:31:35 -0700 Subject: [PATCH 27/28] put binary at root of release zip --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8fd6ca173..1869c261b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,7 +81,7 @@ jobs: ARCHIVE_NAME="${TARGET}.zip" mkdir -p artifacts - (cd dist && zip -9 -r "../artifacts/${ARCHIVE_NAME}" "${TARGET}") + zip -9 -j "artifacts/${ARCHIVE_NAME}" "$OUT_DIR"/* echo "archive-path=artifacts/${ARCHIVE_NAME}" >> "$GITHUB_OUTPUT" From ee2cdf8606e72d4fa16fbb112d0edb05783349e2 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 2 Oct 2025 20:33:49 -0700 Subject: [PATCH 28/28] asset libpath --- internal/bundled/noembed.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/bundled/noembed.go b/internal/bundled/noembed.go index 8a3e29235d..c0db978447 100644 --- a/internal/bundled/noembed.go +++ b/internal/bundled/noembed.go @@ -35,12 +35,7 @@ var libPath = sync.OnceValue(func() string { if testing.Testing() { return TestingLibPath() } - dir := executableDir() - - libdts := filepath.Join(dir, "lib.d.ts") - if _, err := os.Stat(libdts); err != nil { - panic(fmt.Sprintf("bundled: %v does not exist; this executable may be misplaced", libdts)) - } + dir := "asset:///" return tspath.NormalizeSlashes(dir) })