diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..1869c261b3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,154 @@ +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 LICENSE "$OUT_DIR/" + cp NOTICE.txt "$OUT_DIR/" + + ARCHIVE_NAME="${TARGET}.zip" + mkdir -p artifacts + zip -9 -j "artifacts/${ARCHIVE_NAME}" "$OUT_DIR"/* + + 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: + - 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 }} + 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}" + REPO="${GITHUB_REPOSITORY}" + + 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" --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[@]}" --repo "$REPO" --title "$TITLE" --notes "$NOTES" --latest --verify-tag + fi diff --git a/cmd/tsgo/api.go b/cmd/tsgo/api.go index 9438c39115..ae345a0920 100644 --- a/cmd/tsgo/api.go +++ b/cmd/tsgo/api.go @@ -21,12 +21,15 @@ func runAPI(args []string) int { defaultLibraryPath := bundled.LibPath() + logEnabled := os.Getenv("TSGO_LOG_ENABLED") == "1" + s := api.NewServer(&api.ServerOptions{ In: os.Stdin, Out: os.Stdout, Err: os.Stderr, Cwd: *cwd, DefaultLibraryPath: defaultLibraryPath, + LogEnabled: logEnabled, }) if err := s.Run(); err != nil && !errors.Is(err, io.EOF) { 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/api/api.go b/internal/api/api.go index d2bad10599..a21bd986b2 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: @@ -316,5 +339,9 @@ func encodeJSON(v any, err error) ([]byte, error) { if err != nil { return nil, err } - return json.Marshal(v) + b, err := json.Marshal(v) + if err != nil { + return nil, err + } + return b, nil } 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..47cd241da0 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 5a3ce4213b..4e69f462d2 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -8,15 +8,22 @@ import ( "io" "runtime/debug" "strconv" + "strings" "sync" "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/packagejson" "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" ) @@ -58,6 +65,12 @@ const ( CallbackGetAccessibleEntries CallbackReadFile CallbackRealpath + CallbackResolveModuleName + CallbackResolveTypeReferenceDirective + CallbackGetPackageJsonScopeIfApplicable + CallbackGetPackageScopeForPath + CallbackGetImpliedNodeFormatForFile + CallbackIsNodeSourceFile ) type ServerOptions struct { @@ -66,6 +79,7 @@ type ServerOptions struct { Err io.Writer Cwd string DefaultLibraryPath string + LogEnabled bool } var _ vfs.FS = (*Server)(nil) @@ -88,6 +102,252 @@ type Server struct { requestId int } +type hostWrapper struct { + inner project.ProjectHost + server *Server +} + +// 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 newResolverWrapper(h.inner.MakeResolver(host, options, typingsLocation, projectName), h.server) +} + +// 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() +} + +// IsNodeSourceFile implements project.ProjectHost. +func (h *hostWrapper) IsNodeSourceFile(path tspath.Path) bool { + if h.server.CallbackEnabled(CallbackIsNodeSourceFile) { + 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) + } + return res + } + } + 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{ + 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, + } +} + +type PackageJsonIfApplicable struct { + PackageDirectory string `json:"packageDirectory"` + DirectoryExists bool `json:"directoryExists"` + Contents string `json:"contents"` +} + +// 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 *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) + } + return &packagejson.InfoCacheEntry{ + PackageDirectory: res.PackageDirectory, + DirectoryExists: res.DirectoryExists, + Contents: &packagejson.PackageJson{ + Fields: contents, + }, + } + } else { + return nil + } + } + 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 *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) + } + return &packagejson.InfoCacheEntry{ + PackageDirectory: res.PackageDirectory, + DirectoryExists: res.DirectoryExists, + Contents: &packagejson.PackageJson{ + Fields: contents, + }, + } + } + } + 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) +} + +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 { if options.Cwd == "" { panic("Cwd is required") @@ -101,7 +361,13 @@ func NewServer(options *ServerOptions) *Server { fs: bundled.WrapFS(osvfs.FS()), defaultLibraryPath: options.DefaultLibraryPath, } - logger := logging.NewLogger(options.Err) + + var logger logging.Logger + if options.LogEnabled { + logger = logging.NewLogger(options.Err) + } else { + logger = NoLogger{} + } server.logger = logger server.api = NewAPI(&APIInit{ Logger: logger, @@ -111,6 +377,9 @@ func NewServer(options *ServerOptions) *Server { DefaultLibraryPath: options.DefaultLibraryPath, PositionEncoding: lsproto.PositionEncodingKindUTF8, LoggingEnabled: true, + MakeHost: func(currentDirectory string, proj *project.Project, builder *project.ProjectCollectionBuilder, logger *logging.LogTree) project.ProjectHost { + return newProjectHostWrapper(currentDirectory, proj, builder, logger, server) + }, }, }) return server @@ -254,6 +523,18 @@ 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 + case "getImpliedNodeFormatForFile": + s.enabledCallbacks |= CallbackGetImpliedNodeFormatForFile + case "isNodeSourceFile": + s.enabledCallbacks |= CallbackIsNodeSourceFile default: return fmt.Errorf("unknown callback: %s", callback) } @@ -429,7 +710,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) @@ -468,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. @@ -495,3 +777,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/ast/ast.go b/internal/ast/ast.go index 848c717a96..2c264f2a69 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) } @@ -287,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 @@ -10747,6 +10753,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 926d9bd53a..8d0d9a4d20 100644 --- a/internal/ast/symbol.go +++ b/internal/ast/symbol.go @@ -1,9 +1,13 @@ package ast import ( + "iter" + "maps" + "strings" "sync/atomic" "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/tspath" ) // Symbol @@ -25,7 +29,107 @@ 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 @@ -58,3 +162,320 @@ 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 { + 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 { + ret := c.firstTable.Get(name) + if ret != nil { + return ret + } + 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) { + for name, symbol := range c.firstTable.Iter() { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + if !yield(name, symbol) { + break + } + } + } + for name, symbol := range c.secondTable.Iter() { + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + if !yield(name, symbol) { + return + } + } + } + } +} + +// Keys implements SymbolTable. +func (c *CombinedSymbolTable) Keys() iter.Seq[string] { + return func(yield func(string) bool) { + 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 + } + } + } + } +} + +// 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", + "QueuingStrategy", + "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 + getMergedSymbol func(source *Symbol) *Symbol + isNodeSourceFile func(path tspath.Path) bool +} + +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{ + globals: globals, + nodeGlobals: nodeGlobals, + combinedGlobals: &CombinedSymbolTable{ + firstTable: nodeGlobals, + secondTable: globals, + }, + mergeSymbol: mergeSymbol, + getMergedSymbol: getMergedSymbol, + 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 { + } + if isTypesNodeSourceFile && targetSymbol != nil && TypesNodeIgnorableNames.Has(id) && !symbolHasAnyTypesNodePkgDecl(targetSymbol, c.HasNodeSourceFile) { + continue + } + var merged *Symbol + if targetSymbol != nil { + merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) + } else { + merged = c.getMergedSymbol(sourceSymbol) + } + + target.Set(id, merged) + } +} + +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/ast/utilities.go b/internal/ast/utilities.go index 1c65dee66c..bb966aa237 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 8fde6ff763..a2e30cde4f 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 } @@ -189,13 +193,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 +211,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 +954,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 +984,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) { @@ -1221,28 +1225,37 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { 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()] + return symbolTable.Get(name.Text()) } } return nil } if symbol := getInitializerSymbol(b.lookupEntity(node.Expression(), container)); symbol != nil && symbol.Exports != nil { if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbol.Exports[name.Text()] + return symbol.Exports.Get(name.Text()) } } return nil } 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 local := localsContainer.Locals[name]; local != 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 { - return declaration.Symbol.Exports[name] + 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 dbac222247..8e73f66f14 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 @@ -97,7 +99,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 +118,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 } @@ -318,7 +320,12 @@ 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) + } + if result == nil { + result = r.lookup(r.DenoGlobals, name, meaning|ast.SymbolFlagsGlobalLookup) + } } } if result == nil { @@ -426,7 +433,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/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) }) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 54d6c19590..45010cf90e 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 @@ -588,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 @@ -620,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 @@ -856,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 { @@ -886,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 = make(ast.SymbolTable, 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) @@ -917,9 +925,13 @@ 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[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.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.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve c.tupleTypes = make(map[string]*Type) @@ -1086,7 +1098,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 @@ -1251,20 +1263,37 @@ 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.mergeSymbolTable(c.denoGlobals, file.Locals, false,) } 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 { - if _, ok := c.globals[name]; !ok { - c.globals[name] = symbol + for name, symbol := range file.Symbol.GlobalExports.Iter() { + if _, ok := globals.Get2(name); !ok { + globals.Set(name, symbol) } } } @@ -1288,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*/) @@ -1332,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 @@ -1357,14 +1387,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*/) } } } @@ -1379,15 +1409,18 @@ func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) { func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { name := c.undefinedSymbol.Name - targetSymbol := c.globals[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[name] = c.undefinedSymbol + } } @@ -1396,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, @@ -1415,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, @@ -1667,10 +1704,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])) } } @@ -1764,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) @@ -2027,7 +2071,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()) } @@ -2062,7 +2106,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 @@ -3577,7 +3621,7 @@ func (c *Checker) checkBlock(node *ast.Node) { } else { c.checkSourceElements(node.Statements()) } - if len(node.Locals()) != 0 { + if node.Locals() != nil && node.Locals().Len() != 0 { c.registerForUnusedIdentifiersCheck(node) } } @@ -4030,8 +4074,8 @@ func (c *Checker) checkCatchClause(node *ast.Node) { } else { blockLocals := node.AsCatchClause().Block.Locals() if blockLocals != nil { - for caughtName := range node.Locals() { - if blockLocal := blockLocals[caughtName]; blockLocal != nil && blockLocal.ValueDeclaration != nil && blockLocal.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { + for caughtName := range node.Locals().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) } } @@ -4780,7 +4824,7 @@ func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.No return true } seen := make(map[string]InheritanceInfo) - for id, p := range c.resolveDeclaredMembers(t).declaredMembers { + for id, p := range c.resolveDeclaredMembers(t).declaredMembers.Iter() { if c.isNamedMember(p, id) { seen[p.Name] = InheritanceInfo{prop: p, containingType: t} } @@ -5184,11 +5228,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 @@ -5291,7 +5335,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*/) @@ -5417,7 +5461,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) { @@ -5425,7 +5469,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } } // Checks for export * conflicts - for id, symbol := range c.getExportsOfModule(moduleSymbol) { + for id, symbol := range c.getExportsOfModule(moduleSymbol).Iter() { if id == ast.InternalSymbolNameExportStar { continue } @@ -5455,7 +5499,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool { - for id := range moduleSymbol.Exports { + for id := range moduleSymbol.Exports.Iter() { if id != ast.InternalSymbolNameExportEquals { return true } @@ -6237,8 +6281,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() @@ -6419,7 +6463,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)) } @@ -6787,7 +6831,7 @@ 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() { + 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) { @@ -10849,8 +10893,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[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 { @@ -10858,6 +10902,11 @@ 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) { c.reportNonexistentProperty(right, core.IfElse(isThisTypeParameter(leftType), apparentType, leftType)) } @@ -10984,11 +11033,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 } @@ -11613,7 +11662,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 { @@ -11674,7 +11723,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 @@ -12597,9 +12649,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) @@ -12652,7 +12704,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() } @@ -12708,7 +12760,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) @@ -12723,7 +12775,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 @@ -12767,7 +12819,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) } @@ -12779,7 +12831,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 } @@ -12797,7 +12849,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)) } @@ -12862,7 +12914,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 { @@ -12874,7 +12926,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)) } } @@ -12882,8 +12934,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) @@ -12906,10 +12958,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 { @@ -12977,7 +13029,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 @@ -12994,7 +13046,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)) @@ -13454,6 +13506,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 } @@ -13476,21 +13531,57 @@ 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 == nil && second == nil { + return ast.NewSymbolTable() + } else if first == nil { + return second + } else if second == nil { + return first + } + + if first.Len() == 0 { return second } - if len(second) == 0 { + if second.Len() == 0 { return first } - combined := make(ast.SymbolTable) + combined := ast.NewSymbolTable() c.mergeSymbolTable(combined, first, false, nil) c.mergeSymbolTable(combined, second, false, nil) return combined } +// func 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) +// } +// } + +// } + func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { - for id, sourceSymbol := range source { - targetSymbol := target[id] + for id, sourceSymbol := range source.Iter() { + targetSymbol := target.Get(id) var merged *ast.Symbol if targetSymbol != nil { merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) @@ -13517,7 +13608,7 @@ func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTabl merged.Parent = mergedParent } } - target[id] = merged + target.Set(id, merged) } } @@ -13567,7 +13658,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 { @@ -13751,8 +13842,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 } @@ -13935,7 +14026,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 { @@ -13966,13 +14057,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 { @@ -13980,7 +14071,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)) @@ -13990,12 +14081,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, "") @@ -14073,7 +14164,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) @@ -14151,14 +14242,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) @@ -14196,11 +14287,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 @@ -14254,7 +14346,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) @@ -14265,18 +14357,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 @@ -14457,7 +14549,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 @@ -14687,7 +14779,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) } @@ -14867,28 +14959,41 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool if tspath.IsExternalModuleNameRelative(moduleName) { return nil } - symbol := c.getSymbol(c.globals, "\""+moduleName+"\"", ast.SymbolFlagsValueModule) - // merged symbol is module declaration symbol combined with all augmentations + symbol := c.getSymbol(c.denoForkContext.CombinedGlobals(), "\""+moduleName+"\"", ast.SymbolFlagsValueModule) + // module declaration symbol combined with all augmentations if withAugmentations { return c.getMergedSymbol(symbol) } return symbol } -func (c *Checker) GetAmbientModules() []*ast.Symbol { - c.ambientModulesOnce.Do(func() { - for sym, global := range c.globals { - if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { - c.ambientModules = append(c.ambientModules, global) +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.denoForkContext.CombinedGlobals().Iter() { + if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { + c.ambientModules = append(c.ambientModules, global) + } } - } - }) - return c.ambientModules + }) + 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 { 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) } @@ -15018,12 +15123,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) } @@ -15031,8 +15136,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 @@ -15203,7 +15308,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) } @@ -15245,12 +15350,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 */) } @@ -15261,7 +15366,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) } @@ -15321,13 +15426,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. @@ -15363,16 +15468,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, @@ -15430,7 +15535,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 @@ -15439,7 +15544,7 @@ 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 { + for name := range symbol.Exports.Iter() { nonTypeOnlyNames.Add(name) } } @@ -15447,20 +15552,20 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as 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*/) 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 - 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 { @@ -15473,7 +15578,7 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as if typeOnlyExportStarMap == nil { typeOnlyExportStarMap = make(map[string]*ast.Node) } - for name := range symbols { + for name := range symbols.Iter() { typeOnlyExportStarMap[name] = exportStar } } @@ -15483,7 +15588,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) @@ -15496,13 +15601,13 @@ func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports as * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ func (c *Checker) extendExportSymbols(target ast.SymbolTable, source ast.SymbolTable, lookupTable ExportCollisionTable, exportNode *ast.Node) { - for id, sourceSymbol := range source { + for id, sourceSymbol := range source.Iter() { if id == ast.InternalSymbolNameDefault { continue } - targetSymbol := target[id] + targetSymbol := target.Get(id) if targetSymbol == nil { - target[id] = sourceSymbol + target.Set(id, sourceSymbol) if lookupTable != nil && exportNode != nil { lookupTable[id] = &ExportCollision{ specifierText: scanner.GetTextOfNode(exportNode.AsExportDeclaration().ModuleSpecifier), @@ -15628,7 +15733,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) } @@ -15639,7 +15744,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 { @@ -15853,8 +15958,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) @@ -16098,14 +16203,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 @@ -17095,9 +17200,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 @@ -17188,7 +17293,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 { @@ -17207,7 +17312,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 { @@ -17583,14 +17688,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)) } } } @@ -18056,7 +18161,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 @@ -18240,7 +18345,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) { @@ -18260,7 +18369,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 @@ -18275,6 +18384,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) @@ -18282,7 +18394,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) @@ -18751,11 +18863,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) } } } @@ -18768,8 +18880,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 @@ -18778,7 +18890,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 } @@ -18953,7 +19065,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 { @@ -19810,8 +19922,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 @@ -19819,11 +19931,11 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { // Combinations of function, class, enum and module members := c.getExportsOfSymbol(symbol) var indexInfos []*IndexInfo - if symbol == c.globalThisSymbol { - varsOnly := make(ast.SymbolTable) - for _, p := range members { + 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)) { - varsOnly[p.Name] = p + varsOnly.Set(p.Name, p) } } members = varsOnly @@ -19834,16 +19946,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) @@ -19866,7 +19978,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) } @@ -19880,9 +19992,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 } @@ -19890,19 +20002,22 @@ 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 { + 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)) } } } + if result == nil { + panic("result is nil in instantiateSymbolTable") + } return result } @@ -20043,7 +20158,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) @@ -20067,7 +20182,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) @@ -20097,7 +20212,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 @@ -20551,7 +20666,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 } @@ -20580,17 +20695,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) } } } @@ -21232,11 +21347,11 @@ func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { } func (c *Checker) getNamedMembers(members ast.SymbolTable) []*ast.Symbol { - if len(members) == 0 { + if members == nil || members.Len() == 0 { return nil } - result := make([]*ast.Symbol, 0, len(members)) - for id, symbol := range members { + result := make([]*ast.Symbol, 0, members.Len()) + for id, symbol := range members.Iter() { if c.isNamedMember(symbol, id) { result = append(result, symbol) } @@ -22104,7 +22219,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()) @@ -22895,7 +23010,7 @@ func (c *Checker) getOuterTypeParameters(node *ast.Node, includeThisTypes bool) func (c *Checker) getInferTypeParameters(node *ast.Node) []*Type { var result []*Type - for _, symbol := range node.Locals() { + for _, symbol := range node.Locals().Iter() { if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { result = append(result, c.getDeclaredTypeOfSymbol(symbol)) } @@ -23159,7 +23274,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 { @@ -23864,7 +23979,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) @@ -23877,11 +23992,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 @@ -23892,7 +24007,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) @@ -25515,7 +25630,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 { @@ -26167,7 +26282,9 @@ 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.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) { @@ -27071,7 +27188,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 } @@ -27139,14 +27256,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 } @@ -27432,7 +27549,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 @@ -29081,10 +29198,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 @@ -29414,7 +29531,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) @@ -29773,7 +29890,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 { @@ -30268,7 +30385,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 40bd253bc2..0bc6f07d42 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 { @@ -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 { @@ -988,7 +989,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 d395f03a98..cb47548b08 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, 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 31e79c8bde..7826dfbc1b 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 f4b8aa19df..5282377107 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 && @@ -105,12 +104,15 @@ 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() - delete(symbols, ast.InternalSymbolNameThis) // Not a symbol, a keyword + symbols.Delete(ast.InternalSymbolNameThis) // Not a symbol, a keyword return symbolsToArray(symbols) } @@ -119,7 +121,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 +142,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 +200,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 +268,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 +300,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 { diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index d9741f18a5..92532474aa 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 && @@ -506,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 } @@ -608,7 +612,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 +697,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) { @@ -707,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/checker/utilities.go b/internal/checker/utilities.go index e131201893..2a1a7a5ac0 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 } @@ -1278,7 +1278,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. @@ -1778,11 +1778,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/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/fileloader.go b/internal/compiler/fileloader.go index 9fbbc49057..8ee25cba99 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}) } @@ -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, @@ -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/compiler/host.go b/internal/compiler/host.go index 68f3cf620a..03119db8f3 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,8 @@ 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 + IsNodeSourceFile(path tspath.Path) bool } var _ CompilerHost = (*compilerHost)(nil) @@ -74,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 { @@ -86,3 +93,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/compiler/program.go b/internal/compiler/program.go index b70a00197f..fd25d5d5dd 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 f11f06b9fc..3e12c7ac0a 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" @@ -27,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) } @@ -35,6 +41,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..28951e6915 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,15 @@ 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) +} + +// 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/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/execute/incremental/programtosnapshot.go b/internal/execute/incremental/programtosnapshot.go index 7f380f39a8..c8514f5667 100644 --- a/internal/execute/incremental/programtosnapshot.go +++ b/internal/execute/incremental/programtosnapshot.go @@ -293,7 +293,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 4393806e72..c6f8f1e683 100644 --- a/internal/ls/api.go +++ b/internal/ls/api.go @@ -4,10 +4,12 @@ import ( "context" "errors" "fmt" + "slices" "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 ( @@ -42,3 +44,119 @@ func (l *LanguageService) GetTypeOfSymbol(ctx context.Context, symbol *ast.Symbo defer done() 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"` + 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"` + MessageChain []DiagnosticId `json:"messageChain"` + RelatedInformation []DiagnosticId `json:"relatedInformation"` + ReportsUnnecessary bool `json:"reportsUnnecessary"` + ReportsDeprecated bool `json:"reportsDeprecated"` + SkippedOnNoEmit bool `json:"skippedOnNoEmit"` + SourceLine string `json:"sourceLine"` +} + +type diagnosticMaps struct { + diagnosticMapById map[DiagnosticId]Diagnostic + diagnosticReverseMap map[*ast.Diagnostic]DiagnosticId +} + +func (d *diagnosticMaps) addDiagnostic(diagnostic *ast.Diagnostic, ls *LanguageService) DiagnosticId { + if i, ok := d.diagnosticReverseMap[diagnostic]; ok { + return i + } + 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: startPosLineCol, + End: getPosition(diagnostic.File(), diagnostic.Loc().End(), ls), + StartPos: startPos, + EndPos: diagnostic.Loc().End(), + SourceLine: sourceLine, + 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, ls)) + } + + for _, relatedInformation := range diagnostic.RelatedInformation() { + diag.RelatedInformation = append(diag.RelatedInformation, d.addDiagnostic(relatedInformation, ls)) + } + + 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() + diagnosticMaps := &diagnosticMaps{ + diagnosticMapById: make(map[DiagnosticId]Diagnostic), + diagnosticReverseMap: make(map[*ast.Diagnostic]DiagnosticId), + } + diagnostics := make([]*ast.Diagnostic, 0, len(sourceFiles)) + for _, sourceFile := range sourceFiles { + 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, l) + } + return diagnosticMaps.getDiagnostics() +} diff --git a/internal/ls/autoimports.go b/internal/ls/autoimports.go index 7c1d586a28..cfec8707e7 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/ls/completions.go b/internal/ls/completions.go index 820b131203..783813f91e 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 abefaf932e..85ad411aae 100644 --- a/internal/ls/definition.go +++ b/internal/ls/definition.go @@ -125,7 +125,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 d30a4035cf..555b103ace 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/lsp/server.go b/internal/lsp/server.go index 6c3478ae3d..4b2922c4c4 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -660,6 +660,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/module/resolver.go b/internal/module/resolver.go index 780f0deb67..ac38374851 100644 --- a/internal/module/resolver.go +++ b/internal/module/resolver.go @@ -142,6 +142,14 @@ 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 + GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind +} + type Resolver struct { caches host ResolutionHost @@ -151,6 +159,8 @@ type Resolver struct { // reportDiagnostic: DiagnosticReporter } +var _ ResolverInterface = (*Resolver)(nil) + func NewResolver( host ResolutionHost, options *core.CompilerOptions, @@ -267,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 7999e022a3..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 - 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 { @@ -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 { 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/modulespecifiers/types.go b/internal/modulespecifiers/types.go index 6cdfa3e689..d890ae51d1 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/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 } } diff --git a/internal/project/compilerhost.go b/internal/project/compilerhost.go index d58b3522b8..724a6285fd 100644 --- a/internal/project/compilerhost.go +++ b/internal/project/compilerhost.go @@ -6,13 +6,28 @@ 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/module" "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" ) -var _ compiler.CompilerHost = (*compilerHost)(nil) +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) + _ ProjectHost = (*compilerHost)(nil) +) type compilerHost struct { configFilePath tspath.Path @@ -20,15 +35,20 @@ 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 } +// IsNodeSourceFile implements compiler.CompilerHost. +func (c *compilerHost) IsNodeSourceFile(path tspath.Path) bool { + return false +} + type builderFileSource struct { seenFiles *collections.SyncSet[tspath.Path] snapshotFSBuilder *snapshotFSBuilder @@ -44,14 +64,14 @@ func (c *builderFileSource) FS() vfs.FS { return c.snapshotFSBuilder.FS() } -func newCompilerHost( +func NewProjectHost( currentDirectory string, project *Project, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, -) *compilerHost { +) ProjectHost { seenFiles := &collections.SyncSet[tspath.Path]{} - compilerFS := &compilerFS{ + compilerFS := &CompilerFS{ source: &builderFileSource{ seenFiles: seenFiles, snapshotFSBuilder: builder.fs, @@ -75,7 +95,7 @@ func newCompilerHost( // 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") } @@ -135,19 +155,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 } @@ -155,12 +175,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 } @@ -168,36 +188,64 @@ 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") } + +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 +} diff --git a/internal/project/project.go b/internal/project/project.go index 317621294b..14c7298795 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -59,7 +59,7 @@ type Project struct { dirty bool dirtyFilePath tspath.Path - host *compilerHost + host ProjectHost CommandLine *tsoptions.ParsedCommandLine commandLineWithTypingsFiles *tsoptions.ParsedCommandLine commandLineWithTypingsFilesOnce sync.Once @@ -86,7 +86,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) @@ -96,7 +96,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) @@ -131,7 +131,7 @@ func NewProject( configFileName string, kind Kind, currentDirectory string, - builder *projectCollectionBuilder, + builder *ProjectCollectionBuilder, logger *logging.LogTree, ) *Project { if logger != nil { @@ -290,14 +290,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 + if acq := p.GetTypeAcquisition(); acq != nil && acq.Enable.IsTrue() { + typingsLocation = p.host.SessionOptions().TypingsLocation } newProgram = compiler.NewProgram( compiler.ProgramOptions{ diff --git a/internal/project/projectcollectionbuilder.go b/internal/project/projectcollectionbuilder.go index 7717016908..a979415e03 100644 --- a/internal/project/projectcollectionbuilder.go +++ b/internal/project/projectcollectionbuilder.go @@ -25,8 +25,10 @@ const ( projectLoadKindCreate ) -type projectCollectionBuilder struct { +type ProjectCollectionBuilder struct { sessionOptions *SessionOptions + makeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost + host ProjectHost parseCache *ParseCache extendedConfigCache *extendedConfigCache @@ -56,14 +58,16 @@ func newProjectCollectionBuilder( sessionOptions *SessionOptions, parseCache *ParseCache, extendedConfigCache *extendedConfigCache, -) *projectCollectionBuilder { - return &projectCollectionBuilder{ + makeHost func(currentDirectory string, project *Project, builder *ProjectCollectionBuilder, logger *logging.LogTree) ProjectHost, +) *ProjectCollectionBuilder { + return &ProjectCollectionBuilder{ ctx: ctx, fs: fs, compilerOptionsForInferredProjects: compilerOptionsForInferredProjects, sessionOptions: sessionOptions, parseCache: parseCache, extendedConfigCache: extendedConfigCache, + makeHost: makeHost, base: oldProjectCollection, configFileRegistryBuilder: newConfigFileRegistryBuilder(fs, oldConfigFileRegistry, extendedConfigCache, sessionOptions, nil), newSnapshotID: newSnapshotID, @@ -73,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() { @@ -103,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) @@ -117,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) @@ -166,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() @@ -275,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 @@ -326,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 { @@ -371,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 { @@ -403,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 } @@ -420,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 @@ -445,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 @@ -483,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, @@ -641,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, @@ -685,7 +689,7 @@ func (b *projectCollectionBuilder) findOrCreateDefaultConfiguredProjectForOpenSc return searchResult{} } -func (b *projectCollectionBuilder) findOrCreateProject( +func (b *ProjectCollectionBuilder) findOrCreateProject( configFileName string, configFilePath tspath.Path, loadKind projectLoadKind, @@ -699,11 +703,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 { @@ -749,7 +753,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 @@ -781,14 +785,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 = b.sessionOptions.MakeHost(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 @@ -810,7 +814,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( @@ -862,7 +866,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 0b6e537d54..025526ac97 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 { @@ -137,7 +140,7 @@ func NewSession(init *SessionInit) *Session { snapshotID: atomic.Uint64{}, snapshot: NewSnapshot( uint64(0), - &snapshotFS{ + &SnapshotFS{ toPath: toPath, fs: init.FS, }, @@ -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 8d41e426c5..77a35ffc08 100644 --- a/internal/project/snapshot.go +++ b/internal/project/snapshot.go @@ -29,7 +29,7 @@ type Snapshot struct { converters *ls.Converters // Immutable state, cloned between snapshots - fs *snapshotFS + fs *SnapshotFS ProjectCollection *ProjectCollection ConfigFileRegistry *ConfigFileRegistry compilerOptionsForInferredProjects *core.CompilerOptions @@ -41,7 +41,7 @@ type Snapshot struct { // NewSnapshot func NewSnapshot( id uint64, - fs *snapshotFS, + fs *SnapshotFS, sessionOptions *SessionOptions, parseCache *ParseCache, extendedConfigCache *extendedConfigCache, @@ -194,6 +194,7 @@ func (s *Snapshot) Clone(ctx context.Context, change SnapshotChange, overlays ma s.sessionOptions, session.parseCache, session.extendedConfigCache, + session.makeHost, ) var apiError error @@ -231,7 +232,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 } } @@ -277,7 +278,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) { diff --git a/internal/project/snapshotfs.go b/internal/project/snapshotfs.go index 1b45acf0a2..9c590cb1fa 100644 --- a/internal/project/snapshotfs.go +++ b/internal/project/snapshotfs.go @@ -19,10 +19,10 @@ 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 @@ -32,11 +32,11 @@ type snapshotFS struct { type memoizedDiskFile func() *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 } @@ -83,9 +83,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, 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 { diff --git a/internal/tsoptions/enummaps.go b/internal/tsoptions/enummaps.go index faef04dcc8..84ac2c5157 100644 --- a/internal/tsoptions/enummaps.go +++ b/internal/tsoptions/enummaps.go @@ -112,6 +112,24 @@ var LibMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, an {Key: "esnext.sharedmemory", Value: "lib.esnext.sharedmemory.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 (