Skip to content
Merged
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 microsoft/typescript-go
Submodule typescript-go updated 70 files
+7 −2 Herebyfile.mjs
+16 −1 internal/checker/checker.go
+9 −0 internal/checker/emitresolver.go
+10 −0 internal/checker/nodebuilderimpl.go
+6 −3 internal/checker/pseudotypenodebuilder.go
+69 −15 internal/checker/symbolaccessibility.go
+235 −0 internal/fourslash/tests/autoImportDefaultPascalCase_test.go
+39 −0 internal/fourslash/tests/importModuleSpecifierPreference_test.go
+23 −5 internal/ls/autoimport/extract.go
+5 −6 internal/modulespecifiers/specifiers.go
+14 −7 internal/printer/printer.go
+1 −1 internal/printer/utilities.go
+9 −1 internal/transformers/declarations/transform.go
+22 −10 internal/transformers/moduletransforms/commonjsmodule.go
+2 −2 testdata/baselines/reference/compiler/declarationEmitConstObjectLiteralGenericMethod1.js
+20 −0 testdata/baselines/reference/compiler/declarationEmitEnumNaN.js
+23 −0 testdata/baselines/reference/compiler/declarationEmitEnumNaN.symbols
+25 −0 testdata/baselines/reference/compiler/declarationEmitEnumNaN.types
+37 −0 testdata/baselines/reference/compiler/declarationEmitObjectLiteralMethodGenericNoSuffix.js
+50 −0 testdata/baselines/reference/compiler/declarationEmitObjectLiteralMethodGenericNoSuffix.symbols
+39 −0 testdata/baselines/reference/compiler/declarationEmitObjectLiteralMethodGenericNoSuffix.types
+31 −0 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.js
+21 −0 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.symbols
+26 −0 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.types
+15 −0 testdata/baselines/reference/compiler/emptyJSDocComment.js
+7 −0 testdata/baselines/reference/compiler/emptyJSDocComment.symbols
+8 −0 testdata/baselines/reference/compiler/emptyJSDocComment.types
+35 −0 testdata/baselines/reference/compiler/expandoPropertyEmptyArrayWidening.errors.txt
+62 −0 testdata/baselines/reference/compiler/expandoPropertyEmptyArrayWidening.symbols
+79 −0 testdata/baselines/reference/compiler/expandoPropertyEmptyArrayWidening.types
+1 −1 testdata/baselines/reference/compiler/exportDestructuring.js
+24 −0 testdata/baselines/reference/compiler/exportDestructuringIterator.js
+44 −0 testdata/baselines/reference/compiler/exportDestructuringIterator.symbols
+56 −0 testdata/baselines/reference/compiler/exportDestructuringIterator.types
+54 −0 testdata/baselines/reference/compiler/jsDocCommentOfElidedImportPreserved.js
+37 −0 testdata/baselines/reference/compiler/jsDocCommentOfElidedImportPreserved.symbols
+40 −0 testdata/baselines/reference/compiler/jsDocCommentOfElidedImportPreserved.types
+36 −0 testdata/baselines/reference/compiler/symbolToNodeBoundaryNoStackOverflow.errors.txt
+62 −0 testdata/baselines/reference/compiler/symbolToNodeBoundaryNoStackOverflow.symbols
+55 −0 testdata/baselines/reference/compiler/symbolToNodeBoundaryNoStackOverflow.types
+16 −0 testdata/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCase.baseline.md
+16 −0 testdata/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCaseAliasCaseInsensitive.baseline.md
+16 −0 testdata/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCaseAnonymous.baseline.md
+16 −0 ...a/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCaseAnonymousCaseInsensitive.baseline.md
+16 −0 testdata/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCaseCaseInsensitive.baseline.md
+16 −0 ...ta/baselines/reference/fourslash/autoImports/autoImportDefaultPascalCaseReexportCaseInsensitive.baseline.md
+10 −0 ...selines/reference/fourslash/autoImports/importModuleSpecifierPreferenceProjectRelativeWithPaths.baseline.md
+1 −2 testdata/baselines/reference/submodule/compiler/bindingPatternOmittedExpressionNesting.js
+5 −4 testdata/baselines/reference/submodule/compiler/declarationEmitRetainsJsdocyComments.js
+1 −1 testdata/baselines/reference/submodule/compiler/destructuringInVariableDeclarations1.js
+4 −4 testdata/baselines/reference/submodule/compiler/downlevelLetConst13(target=es2015).js
+1 −2 ...ata/baselines/reference/submodule/compiler/exportEmptyArrayBindingPattern(module=commonjs,target=esnext).js
+1 −2 ...ta/baselines/reference/submodule/compiler/exportEmptyObjectBindingPattern(module=commonjs,target=esnext).js
+1 −2 testdata/baselines/reference/submodule/compiler/exportObjectRest(module=commonjs,target=esnext).js
+13 −0 testdata/baselines/reference/submoduleAccepted/compiler/bindingPatternOmittedExpressionNesting.js.diff
+18 −0 testdata/baselines/reference/submoduleAccepted/compiler/declarationEmitRetainsJsdocyComments.js.diff
+11 −0 testdata/baselines/reference/submoduleAccepted/compiler/destructuringInVariableDeclarations1.js.diff
+17 −0 testdata/baselines/reference/submoduleAccepted/compiler/downlevelLetConst13(target=es2015).js.diff
+10 −0 .../reference/submoduleAccepted/compiler/exportEmptyArrayBindingPattern(module=commonjs,target=esnext).js.diff
+10 −0 ...reference/submoduleAccepted/compiler/exportEmptyObjectBindingPattern(module=commonjs,target=esnext).js.diff
+11 −0 ...data/baselines/reference/submoduleAccepted/compiler/exportObjectRest(module=commonjs,target=esnext).js.diff
+10 −0 testdata/submoduleAccepted.txt
+9 −0 testdata/tests/cases/compiler/declarationEmitEnumNaN.ts
+12 −0 testdata/tests/cases/compiler/declarationEmitObjectLiteralMethodGenericNoSuffix.ts
+11 −0 testdata/tests/cases/compiler/declarationEmitReadonlyAsConst.ts
+4 −0 testdata/tests/cases/compiler/emptyJSDocComment.ts
+24 −0 testdata/tests/cases/compiler/expandoPropertyEmptyArrayWidening.ts
+12 −0 testdata/tests/cases/compiler/exportDestructuringIterator.ts
+26 −0 testdata/tests/cases/compiler/jsDocCommentOfElidedImportPreserved.ts
+29 −0 testdata/tests/cases/compiler/symbolToNodeBoundaryNoStackOverflow.ts
17 changes: 16 additions & 1 deletion pkg/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ type Checker struct {
errorTypes map[CacheHashKey]*Type
moduleSymbols map[*ast.Node]*ast.Symbol
globalThisSymbol *ast.Symbol
symbolTableAliasCache map[symbolTableID][]*ast.Symbol
resolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol
resolveNameForSymbolSuggestion func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol
tupleTypes map[CacheHashKey]*Type
Expand Down Expand Up @@ -17984,7 +17985,7 @@ func (c *Checker) getAssignmentDeclarationInitializerType(node *ast.Node) *Type
default:
t = c.checkExpressionForMutableLocation(node.AsBinaryExpression().Right, CheckModeNormal)
}
if c.isEmptyArrayLiteralType(t) {
if c.isEmptyArrayLiteralType(t) && !c.hasParentWithTypeAnnotation(node.Symbol()) {
c.reportImplicitAny(node, c.anyArrayType, WideningKindNormal)
return c.anyArrayType
}
Expand All @@ -17996,6 +17997,20 @@ func (c *Checker) getAssignmentDeclarationInitializerType(node *ast.Node) *Type
return nil
}

// Return true if the parent symbol of the given assignment declaration symbol has declaration with a type
// annotation. For example, returns true for the symbol associated with `f.a` below:
//
// const f: { (): void, a: string[] } = () => {};
// f.a = [];
func (c *Checker) hasParentWithTypeAnnotation(symbol *ast.Symbol) bool {
if symbol.Parent != nil && symbol.Parent.ValueDeclaration != nil && ast.IsFunctionExpressionOrArrowFunction(symbol.Parent.ValueDeclaration) {
if possiblyAnnotatedSymbol := c.getSymbolOfNode(symbol.Parent.ValueDeclaration.Parent); possiblyAnnotatedSymbol != nil && possiblyAnnotatedSymbol.ValueDeclaration != nil {
return possiblyAnnotatedSymbol.ValueDeclaration.Type() != nil
}
}
return false
}

func (c *Checker) containsSameNamedThisProperty(thisProperty *ast.Node, expression *ast.Node) bool {
var visit func(node *ast.Node) bool
visit = func(node *ast.Node) bool {
Expand Down
9 changes: 9 additions & 0 deletions pkg/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,15 @@ func (r *EmitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext,
case string:
return emitContext.Factory.NewStringLiteral(value, ast.TokenFlagsNone)
case jsnum.Number:
if value.IsInf() {
if value > 0 {
return emitContext.Factory.NewIdentifier("Infinity")
}
return emitContext.Factory.NewPrefixUnaryExpression(ast.KindMinusToken, emitContext.Factory.NewIdentifier("Infinity"))
}
if value.IsNaN() {
return emitContext.Factory.NewIdentifier("NaN")
}
if value.Abs() != value {
// negative
return emitContext.Factory.NewPrefixUnaryExpression(
Expand Down
10 changes: 10 additions & 0 deletions pkg/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2693,7 +2693,17 @@ func (b *NodeBuilderImpl) createAnonymousTypeNodeEx(t *Type, forceClassExpansion
// The problem is each constituent of the intersection will be associated with typeof Err<number>
// And when extracting a type for typeof ErrImpl from typeof Err<number> does not make sense.
if ast.IsTypeQueryNode(existing) && b.getTypeFromTypeNode(existing, false) == t {
// Guard against unbounded recursion when the existing typeof node fails to be reused
// (e.g. its entity name isn't accessible from this scope) and the recovery boundary's
// fallback re-enters typeToTypeNode with the very same instantiation type, which would
// in turn try to reuse the same node again. Mark the type as visited around the reuse
// attempt so the inner recursion bottoms out via the visitedTypes guard below.
if b.ctx.visitedTypes.Has(typeId) {
return b.createElidedInformationPlaceholder()
}
b.ctx.visitedTypes.Add(typeId)
typeNode := b.tryReuseExistingNonParameterTypeNode(existing, t, nil, nil)
b.ctx.visitedTypes.Delete(typeId)
if typeNode != nil {
return typeNode
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/checker/pseudotypenodebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,19 +195,19 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
// something a true syntactic ID emitter couldn't possibly know (since the signature could
// be from across files). This can't *really* happen in any cases ID doesn't already error on, though.
// Just something to keep in mind if the ID checker keeps growing.
isConst := b.ch.isConstContext(elements[0].Name)
isConst := b.ch.isConstContext(elements[0].Name.Parent.Parent)
newElements := make([]*ast.Node, 0, len(elements))

for _, e := range elements {
var modifiers *ast.ModifierList
if isConst || (e.Kind == pseudochecker.PseudoObjectElementKindPropertyAssignment && e.AsPseudoPropertyAssignment().Readonly) {
modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)})
}
var cleanup func()
if e.Kind != pseudochecker.PseudoObjectElementKindPropertyAssignment {
signature := b.ch.getSignatureFromDeclaration(e.Signature())
expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0]
cleanup := b.enterNewScope(e.Signature(), expandedParams, signature.typeParameters, signature.parameters, signature.mapper)
defer cleanup()
cleanup = b.enterNewScope(e.Signature(), expandedParams, signature.typeParameters, signature.parameters, signature.mapper)
}
var newProp *ast.Node
switch e.Kind {
Expand Down Expand Up @@ -279,6 +279,9 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
b.e.SetCommentRange(newProp, e.Name.Parent.Loc)
}
newElements = append(newElements, newProp)
if cleanup != nil {
cleanup()
}
}
result := b.f.NewTypeLiteralNode(b.f.NewNodeList(newElements))
if b.ctx.flags&nodebuilder.FlagsMultilineObjectLiterals == 0 {
Expand Down
84 changes: 69 additions & 15 deletions pkg/checker/symbolaccessibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,15 +397,21 @@ type accessibleSymbolChainContext struct {
}

// symbolTableID uniquely identifies a symbol table by encoding its source.
// The high 2 bits encode the kind (locals, exports, members, globals),
// and the remaining bits encode the NodeId or SymbolId of the source.
// The high 3 bits encode the kind, and the remaining bits encode the
// NodeId or SymbolId of the source.
type symbolTableID uint64

const stKindShift = 61

const (
stKindLocals symbolTableID = iota << 62
stKindLocals symbolTableID = iota << stKindShift
stKindExports
stKindMembers
stKindGlobals
stKindResolvedExports // resolved/derived exports from getExportsOfSymbol, distinct from raw sym.Exports

// stKindMask extracts the kind bits from a symbolTableID.
stKindMask symbolTableID = (iota - 1) << stKindShift
)

func symbolTableIDFromLocals(node *ast.Node) symbolTableID {
Expand All @@ -416,6 +422,14 @@ func symbolTableIDFromExports(sym *ast.Symbol) symbolTableID {
return stKindExports | symbolTableID(ast.GetSymbolId(sym))
}

// symbolTableIDFromResolvedExports returns an ID for resolved/derived export tables
// (e.g. from getExportsOfSymbol/getExportsOfModule which may include export * resolution
// and late-bound members). This is distinct from symbolTableIDFromExports to prevent
// cache collisions with raw sym.Exports tables passed by someSymbolTableInScope.
func symbolTableIDFromResolvedExports(sym *ast.Symbol) symbolTableID {
return stKindResolvedExports | symbolTableID(ast.GetSymbolId(sym))
}

func symbolTableIDFromMembers(sym *ast.Symbol) symbolTableID {
return stKindMembers | symbolTableID(ast.GetSymbolId(sym))
}
Expand Down Expand Up @@ -478,31 +492,76 @@ func (c *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolCh
}
visitedSymbolTables[tableId] = struct{}{}

res := c.trySymbolTable(ctx, t, tableId == stKindGlobals, ignoreQualification, isLocalNameLookup)
res := c.trySymbolTable(ctx, t, tableId, ignoreQualification, isLocalNameLookup)

delete(visitedSymbolTables, tableId)
return res
}

// getSymbolTableAliases returns only the alias symbols from a symbol table,
// caching the result by tableId to avoid repeated iteration over large tables.
// Members tables are skipped entirely since someSymbolTableInScope filters them
// to SymbolFlagsType & ^SymbolFlagsAssignment, which never includes aliases.
func (c *Checker) getSymbolTableAliases(symbols ast.SymbolTable, tableId symbolTableID) []*ast.Symbol {
kind := tableId & stKindMask
// Members tables never contain alias symbols; skip entirely.
if kind == stKindMembers {
return nil
}
// Cache globals and exports tables (which are large and revisited often).
// Locals tables are small and per-scope, so they are filtered but not cached.
if kind == stKindGlobals || kind == stKindExports || kind == stKindResolvedExports {
if c.symbolTableAliasCache != nil {
if aliases, ok := c.symbolTableAliasCache[tableId]; ok {
return aliases
}
}
}
var aliases []*ast.Symbol
for _, sym := range symbols {
if sym.Flags&ast.SymbolFlagsAlias != 0 {
aliases = append(aliases, sym)
}
}
if kind == stKindGlobals || kind == stKindExports || kind == stKindResolvedExports {
if c.symbolTableAliasCache == nil {
c.symbolTableAliasCache = make(map[symbolTableID][]*ast.Symbol)
}
c.symbolTableAliasCache[tableId] = aliases
}
return aliases
}

func (c *Checker) trySymbolTable(
ctx accessibleSymbolChainContext,
symbols ast.SymbolTable,
isGlobals bool,
tableId symbolTableID,
ignoreQualification bool,
isLocalNameLookup bool,
) []*ast.Symbol {
isGlobals := tableId == stKindGlobals
// If symbol is directly available by its name in the symbol table
res, ok := symbols[ctx.symbol.Name]
if ok && res != nil && c.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 {

// Check for ExportSymbol by direct name lookup rather than discovering it during
// the alias iteration below (where it would never match, since only alias-flagged
// symbols are iterated).
if ok && res != nil && res.ExportSymbol != nil {
if c.isAccessible(ctx, c.getMergedSymbol(res.ExportSymbol) /*resolvedAliasSymbol*/, nil, ignoreQualification) {
candidateChains = append(candidateChains, []*ast.Symbol{ctx.symbol})
}
}

// Iterate only alias symbols from the table (cached per tableId).
// This avoids iterating thousands of non-alias symbols in large tables like globals.
for _, symbolFromSymbolTable := range c.getSymbolTableAliases(symbols, tableId) {
// 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 &&
if symbolFromSymbolTable.Name != ast.InternalSymbolNameExportEquals &&
symbolFromSymbolTable.Name != ast.InternalSymbolNameDefault &&
!(isUMDExportSymbol(symbolFromSymbolTable) && ctx.enclosingDeclaration != nil && ast.IsExternalModule(ast.GetSourceFileOfNode(ctx.enclosingDeclaration))) &&
// If `!useOnlyExternalAliasing`, we can use any type of alias to get the name
Expand All @@ -518,11 +577,6 @@ func (c *Checker) trySymbolTable(
candidateChains = append(candidateChains, candidate)
}
}
if symbolFromSymbolTable.Name == ctx.symbol.Name && symbolFromSymbolTable.ExportSymbol != nil {
if c.isAccessible(ctx, c.getMergedSymbol(symbolFromSymbolTable.ExportSymbol) /*resolvedAliasSymbol*/, nil, ignoreQualification) {
candidateChains = append(candidateChains, []*ast.Symbol{ctx.symbol})
}
}
}

if len(candidateChains) > 0 {
Expand Down Expand Up @@ -579,7 +633,7 @@ func (c *Checker) getCandidateListForSymbol(
if candidateTable == nil {
return nil
}
candidateTableId := symbolTableIDFromExports(resolvedImportedSymbol)
candidateTableId := symbolTableIDFromResolvedExports(resolvedImportedSymbol)
accessibleSymbolsFromExports := c.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable, candidateTableId /*ignoreQualification*/, true, false)
if len(accessibleSymbolsFromExports) == 0 {
return nil
Expand Down
Loading