Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (sd *snapshotData) newSymbolResponse(symbol *ast.Symbol, canonicalProject P
resp := &SymbolResponse{
Id: id,
Project: project,
Name: ast.EscapeSymbolName(symbol.Name),
Name: symbol.Name.EscapedText(),
Flags: uint32(symbol.Flags),
CheckFlags: uint32(symbol.CheckFlags),
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2517,7 +2517,7 @@ type SourceFile struct {
BindSuggestionDiagnostics []*Diagnostic
EndFlowNode *FlowNode
SymbolCount int
ClassifiableNames collections.Set[string]
ClassifiableNames collections.Set[SymbolNameKey]
PatternAmbientModules []*PatternAmbientModule
GlobalExports SymbolTable

Expand Down
86 changes: 42 additions & 44 deletions internal/ast/symbol.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ast

import (
"strings"
"sync/atomic"
)

Expand All @@ -10,7 +9,7 @@ import (
type Symbol struct {
Flags SymbolFlags
CheckFlags CheckFlags // Non-zero only in transient symbols created by Checker
Name string
Name SymbolNameKey
Declarations []*Node
ValueDeclaration *Node
Members SymbolTable
Expand Down Expand Up @@ -42,62 +41,61 @@ func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags {

// SymbolTable

type SymbolTable map[string]*Symbol
type SymbolNameKey string

const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName
type SymbolTable map[SymbolNameKey]*Symbol

func (name SymbolNameKey) EscapedText() string {
return string(name)
}

func InternalSymbolName(suffix string) SymbolNameKey {
return SymbolNameKey(InternalSymbolNamePrefix + suffix)
}

const InternalSymbolNamePrefix = "__"

const (
InternalSymbolNameCall = InternalSymbolNamePrefix + "call" // Call signatures
InternalSymbolNameConstructor = InternalSymbolNamePrefix + "constructor" // Constructor implementations
InternalSymbolNameNew = InternalSymbolNamePrefix + "new" // Constructor signatures
InternalSymbolNameIndex = InternalSymbolNamePrefix + "index" // Index signatures
InternalSymbolNameExportStar = InternalSymbolNamePrefix + "export" // Module export * declarations
InternalSymbolNameGlobal = InternalSymbolNamePrefix + "global" // Global self-reference
InternalSymbolNameMissing = InternalSymbolNamePrefix + "missing" // Indicates missing symbol
InternalSymbolNameType = InternalSymbolNamePrefix + "type" // Anonymous type literal symbol
InternalSymbolNameObject = InternalSymbolNamePrefix + "object" // Anonymous object literal declaration
InternalSymbolNameJSXAttributes = InternalSymbolNamePrefix + "jsxAttributes" // Anonymous JSX attributes object literal declaration
InternalSymbolNameClass = InternalSymbolNamePrefix + "class" // Unnamed class expression
InternalSymbolNameFunction = InternalSymbolNamePrefix + "function" // Unnamed function expression
InternalSymbolNameComputed = InternalSymbolNamePrefix + "computed" // Computed property name declaration with dynamic name
InternalSymbolNameAssignmentDeclaration = InternalSymbolNamePrefix + "assignment" // Assignment declarations
InternalSymbolNameInstantiationExpression = InternalSymbolNamePrefix + "instantiationExpression" // Instantiation expressions
InternalSymbolNameImportAttributes = InternalSymbolNamePrefix + "importAttributes"
InternalSymbolNameExportEquals = "export=" // Export assignment symbol
InternalSymbolNameDefault = "default" // Default export symbol (technically not wholly internal, but included here for usability)
InternalSymbolNameThis = "this"
InternalSymbolNameModuleExports = "module.exports"
InternalSymbolNameCall SymbolNameKey = InternalSymbolNamePrefix + "call" // Call signatures
InternalSymbolNameConstructor SymbolNameKey = InternalSymbolNamePrefix + "constructor" // Constructor implementations
InternalSymbolNameNew SymbolNameKey = InternalSymbolNamePrefix + "new" // Constructor signatures
InternalSymbolNameIndex SymbolNameKey = InternalSymbolNamePrefix + "index" // Index signatures
InternalSymbolNameExportStar SymbolNameKey = InternalSymbolNamePrefix + "export" // Module export * declarations
InternalSymbolNameGlobal SymbolNameKey = InternalSymbolNamePrefix + "global" // Global self-reference
InternalSymbolNameMissing SymbolNameKey = InternalSymbolNamePrefix + "missing" // Indicates missing symbol
InternalSymbolNameType SymbolNameKey = InternalSymbolNamePrefix + "type" // Anonymous type literal symbol
InternalSymbolNameObject SymbolNameKey = InternalSymbolNamePrefix + "object" // Anonymous object literal declaration
InternalSymbolNameJSXAttributes SymbolNameKey = InternalSymbolNamePrefix + "jsxAttributes" // Anonymous JSX attributes object literal declaration
InternalSymbolNameClass SymbolNameKey = InternalSymbolNamePrefix + "class" // Unnamed class expression
InternalSymbolNameFunction SymbolNameKey = InternalSymbolNamePrefix + "function" // Unnamed function expression
InternalSymbolNameComputed SymbolNameKey = InternalSymbolNamePrefix + "computed" // Computed property name declaration with dynamic name
InternalSymbolNameAssignmentDeclaration SymbolNameKey = InternalSymbolNamePrefix + "assignment" // Assignment declarations
InternalSymbolNameInstantiationExpression SymbolNameKey = InternalSymbolNamePrefix + "instantiationExpression" // Instantiation expressions
InternalSymbolNameImportAttributes SymbolNameKey = InternalSymbolNamePrefix + "importAttributes"
InternalSymbolNameExportEquals SymbolNameKey = "export=" // Export assignment symbol
InternalSymbolNameDefault SymbolNameKey = "default" // Default export symbol (technically not wholly internal, but included here for usability)
InternalSymbolNameThis SymbolNameKey = "this"
InternalSymbolNameModuleExports SymbolNameKey = "module.exports"
)

func SymbolName(symbol *Symbol) string {
if symbol.ValueDeclaration != nil && IsPrivateIdentifierClassElementDeclaration(symbol.ValueDeclaration) {
return symbol.ValueDeclaration.Name().Text()
}
return symbol.Name
return UnescapeLeadingUnderscores(symbol.Name)
}

// EscapeAllInternalSymbolNames replaces internal symbol name markers ("\xFE") with "__".
func EscapeAllInternalSymbolNames(name string) string {
return strings.ReplaceAll(name, InternalSymbolNamePrefix, "__")
}

func EscapeInternalSymbolName(name string) string {
if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok {
return "__" + rest
func EscapeLeadingUnderscores(identifier string) SymbolNameKey {
if len(identifier) >= 2 && identifier[0] == '_' && identifier[1] == '_' {
return SymbolNameKey("_" + identifier)
}
return name
return SymbolNameKey(identifier)
}

// EscapeSymbolName converts a binder symbol name into its escaped "__String"
// form. Internal names (prefixed with the "\xFE" sentinel) become "__"-prefixed,
// and user names that already begin with "__" gain an extra leading underscore
// so they can be distinguished from internal names.
func EscapeSymbolName(name string) string {
if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok {
return "__" + rest
}
if len(name) >= 2 && name[0] == '_' && name[1] == '_' {
return "_" + name
func UnescapeLeadingUnderscores(identifier SymbolNameKey) string {
name := string(identifier)
if len(name) >= 3 && name[0] == '_' && name[1] == '_' && name[2] == '_' {
return name[1:]
}
return name
}
10 changes: 5 additions & 5 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2537,7 +2537,7 @@ func GetNamespaceDeclarationNode(node *Node) *Node {
}

func ModuleExportNameIsDefault(node *Node) bool {
return node.Text() == InternalSymbolNameDefault
return EscapeLeadingUnderscores(node.Text()) == InternalSymbolNameDefault
}

func IsDefaultImport(node *Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) bool {
Expand Down Expand Up @@ -3157,22 +3157,22 @@ func isShorthandPropertyNameUseSite(useSite *Node) bool {
return IsIdentifier(useSite) && IsShorthandPropertyAssignment(useSite.Parent) && useSite.Parent.AsShorthandPropertyAssignment().Name() == useSite
}

func GetPropertyNameForPropertyNameNode(name *Node) string {
func GetPropertyNameForPropertyNameNode(name *Node) SymbolNameKey {
switch name.Kind {
case KindIdentifier, KindPrivateIdentifier, KindStringLiteral, KindNoSubstitutionTemplateLiteral,
KindNumericLiteral, KindBigIntLiteral, KindJsxNamespacedName:
return name.Text()
return EscapeLeadingUnderscores(name.Text())
case KindComputedPropertyName:
nameExpression := name.Expression()
if IsStringOrNumericLiteralLike(nameExpression) {
return nameExpression.Text()
return EscapeLeadingUnderscores(nameExpression.Text())
}
if IsSignedNumericLiteral(nameExpression) {
text := nameExpression.AsPrefixUnaryExpression().Operand.Text()
if nameExpression.AsPrefixUnaryExpression().Operator == KindMinusToken {
text = "-" + text
}
return text
return EscapeLeadingUnderscores(text)
}
return InternalSymbolNameMissing
}
Expand Down
54 changes: 28 additions & 26 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ type Binder struct {
inAssignmentPattern bool
seenParseError bool
symbolCount int
classifiableNames collections.Set[string]
classifiableNames collections.Set[ast.SymbolNameKey]
notConstEnumOnlyModules collections.Set[*ast.Symbol]
symbolArena core.Arena[ast.Symbol]
flowNodeArena core.Arena[ast.FlowNode]
Expand Down Expand Up @@ -131,7 +131,7 @@ func bindSourceFile(file *ast.SourceFile) {
})
}

func (b *Binder) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol {
func (b *Binder) newSymbol(flags ast.SymbolFlags, name ast.SymbolNameKey) *ast.Symbol {
b.symbolCount++
result := b.symbolArena.New()
result.Flags = flags
Expand All @@ -155,7 +155,7 @@ func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol
debug.Assert(isComputedName || !ast.HasDynamicName(node))
isDefaultExport := ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) || ast.IsExportSpecifier(node) && ast.ModuleExportNameIsDefault(node.AsExportSpecifier().Name())
// The exported symbol for an export default function/class node is always named "default"
var name string
var name ast.SymbolNameKey
switch {
case isComputedName:
name = ast.InternalSymbolNameComputed
Expand Down Expand Up @@ -303,7 +303,7 @@ func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol

// Should not be called on a declaration with a computed property name,
// unless it is a well known Symbol.
func (b *Binder) getDeclarationName(node *ast.Node) string {
func (b *Binder) getDeclarationName(node *ast.Node) ast.SymbolNameKey {
if ast.IsExportAssignment(node) {
return core.IfElse(node.AsExportAssignment().IsExportEquals, ast.InternalSymbolNameExportEquals, ast.InternalSymbolNameDefault)
}
Expand All @@ -314,7 +314,7 @@ func (b *Binder) getDeclarationName(node *ast.Node) string {
if ast.IsGlobalScopeAugmentation(node) {
return ast.InternalSymbolNameGlobal
}
return "\"" + moduleName + "\""
return ast.EscapeLeadingUnderscores("\"" + moduleName + "\"")
}
if ast.IsPrivateIdentifier(name) {
// containingClass exists because private names only allowed inside classes
Expand All @@ -326,17 +326,17 @@ func (b *Binder) getDeclarationName(node *ast.Node) string {
return GetSymbolNameForPrivateIdentifier(containingClass.Symbol(), name.Text())
}
if ast.IsPropertyNameLiteral(name) || ast.IsJsxNamespacedName(name) {
return name.Text()
return ast.EscapeLeadingUnderscores(name.Text())
}
if ast.IsComputedPropertyName(name) {
nameExpression := name.Expression()
// treat computed property names where expression is string/numeric literal as just string/numeric literal
if ast.IsStringOrNumericLiteralLike(nameExpression) {
return nameExpression.Text()
return ast.EscapeLeadingUnderscores(nameExpression.Text())
}
if ast.IsSignedNumericLiteral(nameExpression) {
unaryExpression := nameExpression.AsPrefixUnaryExpression()
return scanner.TokenToString(unaryExpression.Operator) + unaryExpression.Operand.Text()
return ast.EscapeLeadingUnderscores(scanner.TokenToString(unaryExpression.Operator) + unaryExpression.Operand.Text())
}
panic("Only computed properties with literal names have declaration names")
}
Expand Down Expand Up @@ -366,13 +366,13 @@ func (b *Binder) getDisplayName(node *ast.Node) string {
}
name := b.getDeclarationName(node)
if name != ast.InternalSymbolNameMissing {
return name
return ast.UnescapeLeadingUnderscores(name)
}
return "(Missing)"
}

func GetSymbolNameForPrivateIdentifier(containingClassSymbol *ast.Symbol, description string) string {
return ast.InternalSymbolNamePrefix + "#" + strconv.Itoa(int(ast.GetSymbolId(containingClassSymbol))) + "@" + description
func GetSymbolNameForPrivateIdentifier(containingClassSymbol *ast.Symbol, description string) ast.SymbolNameKey {
return ast.InternalSymbolName("#" + strconv.Itoa(int(ast.GetSymbolId(containingClassSymbol))) + "@" + description)
}

func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol {
Expand Down Expand Up @@ -770,7 +770,7 @@ func (b *Binder) bindSourceFileIfExternalModule() {
}

func (b *Binder) bindSourceFileAsExternalModule() {
b.bindAnonymousDeclaration(b.file.AsNode(), ast.SymbolFlagsValueModule, "\""+tspath.RemoveFileExtension(b.file.FileName())+"\"")
b.bindAnonymousDeclaration(b.file.AsNode(), ast.SymbolFlagsValueModule, ast.EscapeLeadingUnderscores("\""+tspath.RemoveFileExtension(b.file.FileName())+"\""))
}

func (b *Binder) bindModuleDeclaration(node *ast.Node) {
Expand Down Expand Up @@ -916,7 +916,7 @@ func (b *Binder) bindFunctionExpression(node *ast.Node) {
bindingName := ast.InternalSymbolNameFunction
if ast.IsFunctionExpression(node) && node.AsFunctionExpression().Name() != nil {
b.checkStrictModeFunctionName(node)
bindingName = node.AsFunctionExpression().Name().Text()
bindingName = ast.EscapeLeadingUnderscores(node.AsFunctionExpression().Name().Text())
}
b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunction, bindingName)
}
Expand Down Expand Up @@ -950,7 +950,7 @@ func (b *Binder) bindClassLikeDeclaration(node *ast.Node) {
case ast.KindClassExpression:
nameText := ast.InternalSymbolNameClass
if name != nil {
nameText = name.Text()
nameText = ast.EscapeLeadingUnderscores(name.Text())
b.classifiableNames.Add(nameText)
}
b.bindAnonymousDeclaration(node, ast.SymbolFlagsClass, nameText)
Expand All @@ -965,7 +965,7 @@ func (b *Binder) bindClassLikeDeclaration(node *ast.Node) {
// Note: we check for this here because this class may be merging into a module. The
// 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")
prototypeSymbol := b.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsPrototype, ast.EscapeLeadingUnderscores("prototype"))
symbolExport := ast.GetExports(symbol)[prototypeSymbol.Name]
if symbolExport != nil {
b.errorOnNode(symbolExport.Declarations[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol))
Expand Down Expand Up @@ -1201,7 +1201,7 @@ func (b *Binder) bindParameter(node *ast.Node) {
}
if ast.IsBindingPattern(decl.Name()) {
index := slices.Index(node.Parent.Parameters(), node)
b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunctionScopedVariable, "__"+strconv.Itoa(index))
b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunctionScopedVariable, ast.InternalSymbolName(strconv.Itoa(index)))
} else {
b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsParameterExcludes)
}
Expand Down Expand Up @@ -1233,7 +1233,7 @@ func (b *Binder) getInferTypeContainer(node *ast.Node) *ast.Node {
return nil
}

func (b *Binder) bindAnonymousDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, name string) {
func (b *Binder) bindAnonymousDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, name ast.SymbolNameKey) {
symbol := b.newSymbol(symbolFlags, name)
if symbolFlags&(ast.SymbolFlagsEnumMember|ast.SymbolFlagsClassMember) != 0 {
symbol.Parent = b.container.Symbol()
Expand Down Expand Up @@ -1276,27 +1276,28 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol {
if node.Expression().Kind == ast.KindThisKeyword {
if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil {
if name := ast.GetElementOrPropertyAccessName(node); name != nil {
return symbolTable[name.Text()]
return symbolTable[ast.EscapeLeadingUnderscores(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[ast.EscapeLeadingUnderscores(name.Text())]
}
}
return nil
}

func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol {
symbolName := ast.EscapeLeadingUnderscores(name)
if localsContainer := container.LocalsContainerData(); localsContainer != nil {
if local := localsContainer.Locals[name]; local != nil {
if local := localsContainer.Locals[symbolName]; local != nil {
return core.OrElse(local.ExportSymbol, local)
}
}
if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil {
return declaration.Symbol.Exports[name]
return declaration.Symbol.Exports[symbolName]
}
return nil
}
Expand Down Expand Up @@ -1625,20 +1626,21 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) {
}

func (b *Binder) declareCommonJSVariable(name string) {
symbolName := ast.EscapeLeadingUnderscores(name)
locals := ast.GetLocals(b.file.AsNode())
if locals[name] == nil {
symbol := b.newSymbol(ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsModuleExports, name)
if locals[symbolName] == nil {
symbol := b.newSymbol(ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsModuleExports, symbolName)
symbol.Declarations = b.newSingleDeclaration(b.file.AsNode())
symbol.ValueDeclaration = symbol.Declarations[0]
if name == "module" {
exportsProperty := b.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsProperty, "exports")
exportsProperty := b.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsProperty, ast.EscapeLeadingUnderscores("exports"))
exportsProperty.Declarations = symbol.Declarations
exportsProperty.ValueDeclaration = symbol.ValueDeclaration
exportsProperty.Parent = symbol
symbol.Members = make(ast.SymbolTable, 1)
symbol.Members["exports"] = exportsProperty
symbol.Members[ast.EscapeLeadingUnderscores("exports")] = exportsProperty
}
locals[name] = symbol
locals[symbolName] = symbol
}
}

Expand Down
Loading