diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 52206b9237..91fefba966 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -998,6 +998,16 @@ func (n *Node) ModuleSpecifier() *Expression { panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String()) } +func (n *Node) ImportClause() *Node { + switch n.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return n.AsImportDeclaration().ImportClause + case KindJSDocImportTag: + return n.AsJSDocImportTag().ImportClause + } + panic("Unhandled case in Node.ImportClause: " + n.Kind.String()) +} + func (n *Node) Statement() *Statement { switch n.Kind { case KindDoStatement: @@ -10117,6 +10127,10 @@ func (node *JSDocCallbackTag) Clone(f NodeFactoryCoercible) *Node { return cloneNode(f.AsNodeFactory().NewJSDocCallbackTag(node.TagName, node.TypeExpression, node.FullName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) } +func IsJSDocCallbackTag(node *Node) bool { + return node.Kind == KindJSDocCallbackTag +} + // JSDocOverloadTag type JSDocOverloadTag struct { JSDocTagBase @@ -10190,6 +10204,10 @@ func (node *JSDocTypedefTag) Clone(f NodeFactoryCoercible) *Node { func (node *JSDocTypedefTag) Name() *DeclarationName { return node.name } +func IsJSDocTypedefTag(node *Node) bool { + return node.Kind == KindJSDocTypedefTag +} + // JSDocTypeLiteral type JSDocTypeLiteral struct { TypeNodeBase diff --git a/internal/checker/exports.go b/internal/checker/exports.go index 5481b1f31c..83d4352c8e 100644 --- a/internal/checker/exports.go +++ b/internal/checker/exports.go @@ -150,3 +150,7 @@ func (c *Checker) GetIndexSignaturesAtLocation(node *ast.Node) []*ast.Node { func (c *Checker) GetResolvedSymbol(node *ast.Node) *ast.Symbol { return c.getResolvedSymbol(node) } + +func (c *Checker) GetImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { + return c.getImmediateAliasedSymbol(symbol) +} diff --git a/internal/checker/services.go b/internal/checker/services.go index ed7286203c..95edc6e39d 100644 --- a/internal/checker/services.go +++ b/internal/checker/services.go @@ -347,14 +347,14 @@ func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn fu func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol { roots := c.getImmediateRootSymbols(symbol) - if roots != nil { - var result []*ast.Symbol - for _, root := range roots { - result = append(result, c.GetRootSymbols(root)...) - } - return result + if len(roots) == 0 { + return []*ast.Symbol{symbol} } - return []*ast.Symbol{symbol} + var result []*ast.Symbol + for _, root := range roots { + result = append(result, c.GetRootSymbols(root)...) + } + return result } func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { @@ -364,7 +364,8 @@ func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { func(t *Type) *ast.Symbol { return c.getPropertyOfType(t, symbol.Name) }) - } else if symbol.Flags&ast.SymbolFlagsTransient != 0 { + } + if symbol.Flags&ast.SymbolFlagsTransient != 0 { if c.spreadLinks.Has(symbol) { leftSpread := c.spreadLinks.Get(symbol).leftSpread rightSpread := c.spreadLinks.Get(symbol).rightSpread @@ -382,9 +383,7 @@ func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { if target != nil { return []*ast.Symbol{target} } - return nil } - return nil } @@ -418,20 +417,16 @@ func (c *Checker) GetExportSpecifierLocalTargetSymbol(node *ast.Node) *ast.Symbo if node.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil { return c.getExternalModuleMember(node.Parent.Parent, node, false /*dontResolveAlias*/) } - name := node.PropertyName() - if name == nil { - name = node.Name() - } + name := node.PropertyNameOrName() if name.Kind == ast.KindStringLiteral { // Skip for invalid syntax like this: export { "x" } return nil } + return c.resolveEntityName(name, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) case ast.KindIdentifier: - // do nothing (don't panic) - default: - panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier") + return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) } - return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) + panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier") } func (c *Checker) GetShorthandAssignmentValueSymbol(location *ast.Node) *ast.Symbol { @@ -683,3 +678,122 @@ func (c *Checker) GetFirstTypeArgumentFromKnownType(t *Type) *Type { } return nil } + +// Gets all symbols for one property. Does not get symbols for every property. +func (c *Checker) GetPropertySymbolsFromContextualType(node *ast.Node, contextualType *Type, unionSymbolOk bool) []*ast.Symbol { + name := ast.GetTextOfPropertyName(node.Name()) + if name == "" { + return nil + } + if contextualType.flags&TypeFlagsUnion == 0 { + if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { + return []*ast.Symbol{symbol} + } + return nil + } + filteredTypes := contextualType.Types() + if ast.IsObjectLiteralExpression(node.Parent) || ast.IsJsxAttributes(node.Parent) { + filteredTypes = core.Filter(filteredTypes, func(t *Type) bool { + return !c.IsTypeInvalidDueToUnionDiscriminant(t, node.Parent) + }) + } + discriminatedPropertySymbols := core.MapNonNil(filteredTypes, func(t *Type) *ast.Symbol { + return c.getPropertyOfType(t, name) + }) + if unionSymbolOk && (len(discriminatedPropertySymbols) == 0 || len(discriminatedPropertySymbols) == len(contextualType.Types())) { + if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { + return []*ast.Symbol{symbol} + } + } + if len(filteredTypes) == 0 && len(discriminatedPropertySymbols) == 0 { + // Bad discriminant -- do again without discriminating + return core.MapNonNil(contextualType.Types(), func(t *Type) *ast.Symbol { + return c.getPropertyOfType(t, name) + }) + } + // by eliminating duplicates we might even end up with a single symbol + // that helps with displaying better quick infos on properties of union types + return core.Deduplicate(discriminatedPropertySymbols) +} + +// Gets the property symbol corresponding to the property in destructuring assignment +// 'property1' from +// +// for ( { property1: a } of elems) { +// } +// +// 'property1' at location 'a' from: +// +// [a] = [ property1, property2 ] +func (c *Checker) GetPropertySymbolOfDestructuringAssignment(location *ast.Node) *ast.Symbol { + if isArrayLiteralOrObjectLiteralDestructuringPattern(location.Parent.Parent) { + // Get the type of the object or array literal and then look for property of given name in the type + if typeOfObjectLiteral := c.getTypeOfAssignmentPattern(location.Parent.Parent); typeOfObjectLiteral != nil { + return c.getPropertyOfType(typeOfObjectLiteral, location.Text()) + } + } + return nil +} + +// Gets the type of object literal or array literal of destructuring assignment. +// { a } from +// +// for ( { a } of elems) { +// } +// +// [ a ] from +// +// [a] = [ some array ...] +func (c *Checker) getTypeOfAssignmentPattern(expr *ast.Node) *Type { + // If this is from "for of" + // for ( { a } of elems) { + // } + if ast.IsForOfStatement(expr.Parent) { + iteratedType := c.checkRightHandSideOfForOf(expr.Parent) + return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) + } + // If this is from "for" initializer + // for ({a } = elems[0];.....) { } + if ast.IsBinaryExpression(expr.Parent) { + iteratedType := c.getTypeOfExpression(expr.Parent.AsBinaryExpression().Right) + return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) + } + // If this is from nested object binding pattern + // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { + if ast.IsPropertyAssignment(expr.Parent) { + node := expr.Parent.Parent + typeOfParentObjectLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) + propertyIndex := slices.Index(node.AsObjectLiteralExpression().Properties.Nodes, expr.Parent) + return c.checkObjectLiteralDestructuringPropertyAssignment(node, typeOfParentObjectLiteral, propertyIndex, nil, false) + } + // Array literal assignment - array destructuring pattern + node := expr.Parent + // [{ property1: p1, property2 }] = elems; + typeOfArrayLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) + elementType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, typeOfArrayLiteral, c.undefinedType, expr.Parent), c.errorType) + return c.checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, slices.Index(node.AsArrayLiteralExpression().Elements.Nodes, expr), elementType, CheckModeNormal) +} + +func isArrayLiteralOrObjectLiteralDestructuringPattern(node *ast.Node) bool { + if !(ast.IsArrayLiteralExpression(node) || ast.IsObjectLiteralExpression(node)) { + return false + } + parent := node.Parent + // [a,b,c] from: + // [a, b, c] = someExpression; + if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().Left == node && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken { + return true + } + // [a, b, c] from: + // for([a, b, c] of expression) + if ast.IsForOfStatement(parent) && parent.Initializer() == node { + return true + } + // {x, a: {a, b, c} } = someExpression + if ast.IsPropertyAssignment(parent) { + return isArrayLiteralOrObjectLiteralDestructuringPattern(parent.Parent) + } + // [a, b, c] of + // [x, [a, b, c] ] = someExpression + return isArrayLiteralOrObjectLiteralDestructuringPattern(parent) +} diff --git a/internal/core/core.go b/internal/core/core.go index e5579f6e57..a5528d8088 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -351,12 +351,6 @@ func Coalesce[T *U, U any](a T, b T) T { } } -// Returns the first element that is not `nil`; CoalesceList(a, b, c) is roughly analogous to `a ?? b ?? c` in JS, except that it -// non-shortcutting, so it is advised to only use a constant or precomputed value for non-first values in the list -func CoalesceList[T *U, U any](a ...T) T { - return FirstNonNil(a, func(t T) T { return t }) -} - func ComputeLineStarts(text string) []TextPos { result := make([]TextPos, 0, strings.Count(text, "\n")+1) return slices.AppendSeq(result, ComputeLineStartsSeq(text)) @@ -625,3 +619,21 @@ func CopyMapInto[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) maps.Copy(dst, src) return dst } + +func Deduplicate[T comparable](slice []T) []T { + if len(slice) > 1 { + for i, value := range slice { + if slices.Contains(slice[:i], value) { + result := slices.Clone(slice[:i]) + for i++; i < len(slice); i++ { + value = slice[i] + if !slices.Contains(result, value) { + result = append(result, value) + } + } + return result + } + } + } + return slice +} diff --git a/internal/ls/findallreferences.go b/internal/ls/findallreferences.go index 192c2bad9d..5e494c7d75 100644 --- a/internal/ls/findallreferences.go +++ b/internal/ls/findallreferences.go @@ -6,7 +6,6 @@ import ( "fmt" "slices" "strings" - "unicode/utf8" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/astnav" @@ -18,6 +17,7 @@ import ( "github.com/microsoft/typescript-go/internal/debug" "github.com/microsoft/typescript-go/internal/lsp/lsproto" "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/stringutil" "github.com/microsoft/typescript-go/internal/tspath" ) @@ -107,10 +107,20 @@ type referenceEntry struct { } func (l *LanguageService) getRangeOfEntry(entry *referenceEntry) *lsproto.Range { + return l.resolveEntry(entry).textRange +} + +func (l *LanguageService) getFileNameOfEntry(entry *referenceEntry) string { + return l.resolveEntry(entry).fileName +} + +func (l *LanguageService) resolveEntry(entry *referenceEntry) *referenceEntry { if entry.textRange == nil { - entry.textRange = l.getRangeOfNode(entry.node, nil, nil) + sourceFile := ast.GetSourceFileOfNode(entry.node) + entry.textRange = l.getRangeOfNode(entry.node, sourceFile, nil /*endNode*/) + entry.fileName = sourceFile.FileName() } - return entry.textRange + return entry } func (l *LanguageService) newRangeEntry(file *ast.SourceFile, start, end int) *referenceEntry { @@ -444,6 +454,82 @@ func (l *LanguageService) getImplementationReferenceEntries(ctx context.Context, return core.FlatMap(symbolsAndEntries, func(s *SymbolAndEntries) []*referenceEntry { return s.references }) } +func (l *LanguageService) ProvideRename(ctx context.Context, params *lsproto.RenameParams) (lsproto.WorkspaceEditOrNull, error) { + program, sourceFile := l.getProgramAndFile(params.TextDocument.Uri) + position := int(l.converters.LineAndCharacterToPosition(sourceFile, params.Position)) + node := astnav.GetTouchingPropertyName(sourceFile, position) + if node.Kind != ast.KindIdentifier { + return lsproto.WorkspaceEditOrNull{}, nil + } + options := refOptions{use: referenceUseRename, useAliasesForRename: true} + symbolsAndEntries := l.getReferencedSymbolsForNode(ctx, position, node, program, program.GetSourceFiles(), options, nil) + entries := core.FlatMap(symbolsAndEntries, func(s *SymbolAndEntries) []*referenceEntry { return s.references }) + changes := make(map[lsproto.DocumentUri][]*lsproto.TextEdit) + checker, done := program.GetTypeChecker(ctx) + defer done() + for _, entry := range entries { + uri := FileNameToDocumentURI(l.getFileNameOfEntry(entry)) + textEdit := &lsproto.TextEdit{ + Range: *l.getRangeOfEntry(entry), + NewText: l.getTextForRename(node, entry, params.NewName, checker), + } + changes[uri] = append(changes[uri], textEdit) + } + return lsproto.WorkspaceEditOrNull{ + WorkspaceEdit: &lsproto.WorkspaceEdit{ + Changes: &changes, + }, + }, nil +} + +func (l *LanguageService) getTextForRename(originalNode *ast.Node, entry *referenceEntry, newText string, checker *checker.Checker) string { + if entry.kind != entryKindRange && (ast.IsIdentifier(originalNode) || ast.IsStringLiteralLike(originalNode)) { + node := entry.node + kind := entry.kind + parent := node.Parent + name := originalNode.Text() + isShorthandAssignment := ast.IsShorthandPropertyAssignment(parent) + switch { + case isShorthandAssignment || (isObjectBindingElementWithoutPropertyName(parent) && parent.Name() == node && parent.AsBindingElement().DotDotDotToken == nil): + if kind == entryKindSearchedLocalFoundProperty { + return name + ": " + newText + } + if kind == entryKindSearchedPropertyFoundLocal { + return newText + ": " + name + } + // In `const o = { x }; o.x`, symbolAtLocation at `x` in `{ x }` is the property symbol. + // For a binding element `const { x } = o;`, symbolAtLocation at `x` is the property symbol. + if isShorthandAssignment { + grandParent := parent.Parent + if ast.IsObjectLiteralExpression(grandParent) && ast.IsBinaryExpression(grandParent.Parent) && ast.IsModuleExportsAccessExpression(grandParent.Parent.AsBinaryExpression().Left) { + return name + ": " + newText + } + return newText + ": " + name + } + return name + ": " + newText + case ast.IsImportSpecifier(parent) && parent.PropertyName() == nil: + // If the original symbol was using this alias, just rename the alias. + var originalSymbol *ast.Symbol + if ast.IsExportSpecifier(originalNode.Parent) { + originalSymbol = checker.GetExportSpecifierLocalTargetSymbol(originalNode.Parent) + } else { + originalSymbol = checker.GetSymbolAtLocation(originalNode) + } + if slices.Contains(originalSymbol.Declarations, parent) { + return name + " as " + newText + } + return newText + case ast.IsExportSpecifier(parent) && parent.PropertyName() == nil: + // If the symbol for the node is same as declared node symbol use prefix text + if originalNode == entry.node || checker.GetSymbolAtLocation(originalNode) == checker.GetSymbolAtLocation(entry.node) { + return name + " as " + newText + } + return newText + " as " + name + } + } + return newText +} + // == functions for conversions == func (l *LanguageService) convertSymbolAndEntriesToLocations(s *SymbolAndEntries) []lsproto.Location { return l.convertEntriesToLocations(s.references) @@ -452,14 +538,9 @@ func (l *LanguageService) convertSymbolAndEntriesToLocations(s *SymbolAndEntries func (l *LanguageService) convertEntriesToLocations(entries []*referenceEntry) []lsproto.Location { locations := make([]lsproto.Location, len(entries)) for i, entry := range entries { - if entry.textRange == nil { - sourceFile := ast.GetSourceFileOfNode(entry.node) - entry.textRange = l.getRangeOfNode(entry.node, sourceFile, nil /*endNode*/) - entry.fileName = sourceFile.FileName() - } locations[i] = lsproto.Location{ - Uri: FileNameToDocumentURI(entry.fileName), - Range: *entry.textRange, + Uri: FileNameToDocumentURI(l.getFileNameOfEntry(entry)), + Range: *l.getRangeOfEntry(entry), } } return locations @@ -532,15 +613,15 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit } } + checker, done := program.GetTypeChecker(ctx) + defer done() + if node.Kind == ast.KindSourceFile { resolvedRef := getReferenceAtPosition(node.AsSourceFile(), position, program) if resolvedRef.file == nil { return nil } - checker, done := program.GetTypeChecker(ctx) - defer done() - if moduleSymbol := checker.GetMergedSymbol(resolvedRef.file.Symbol); moduleSymbol != nil { return getReferencedSymbolsForModule(program, moduleSymbol /*excludeImportTypeOfExportEquals*/, false, sourceFiles, sourceFilesSet) } @@ -563,9 +644,6 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit } } - checker, done := program.GetTypeChecker(ctx) - defer done() - // constructors should use the class symbol, detected by name, if present symbol := checker.GetSymbolAtLocation(core.IfElse(node.Kind == ast.KindConstructor && node.Parent.Name() != nil, node.Parent.Name(), node)) // Could not find a symbol e.g. unknown identifier @@ -596,19 +674,19 @@ func (l *LanguageService) getReferencedSymbolsForNode(ctx context.Context, posit return getReferencedSymbolsForModule(program, symbol.Parent, false /*excludeImportTypeOfExportEquals*/, sourceFiles, sourceFilesSet) } - moduleReferences := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, symbol, program, sourceFiles, options, sourceFilesSet) // !!! cancellationToken + moduleReferences := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, symbol, program, sourceFiles, checker, options, sourceFilesSet) // !!! cancellationToken if moduleReferences != nil && symbol.Flags&ast.SymbolFlagsTransient != 0 { return moduleReferences } aliasedSymbol := getMergedAliasedSymbolOfNamespaceExportDeclaration(node, symbol, checker) - moduleReferencesOfExportTarget := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, aliasedSymbol, program, sourceFiles, options, sourceFilesSet) // !!! cancellationToken + moduleReferencesOfExportTarget := l.getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx, aliasedSymbol, program, sourceFiles, checker, options, sourceFilesSet) // !!! cancellationToken references := getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, options) // !!! cancellationToken return l.mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget) } -func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx context.Context, symbol *ast.Symbol, program *compiler.Program, sourceFiles []*ast.SourceFile, options refOptions, sourceFilesSet *collections.Set[string]) []*SymbolAndEntries { +func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ctx context.Context, symbol *ast.Symbol, program *compiler.Program, sourceFiles []*ast.SourceFile, checker *checker.Checker, options refOptions, sourceFilesSet *collections.Set[string]) []*SymbolAndEntries { moduleSourceFileName := "" if symbol == nil || !((symbol.Flags&ast.SymbolFlagsModule != 0) && len(symbol.Declarations) != 0) { return nil @@ -624,10 +702,6 @@ func (l *LanguageService) getReferencedSymbolsForModuleIfDeclaredBySourceFile(ct if exportEquals == nil || !sourceFilesSet.Has(moduleSourceFileName) { return moduleReferences } - // Continue to get references to 'export ='. - checker, done := program.GetTypeChecker(ctx) - defer done() - symbol, _ = checker.ResolveAlias(exportEquals) return l.mergeReferences(program, moduleReferences, getReferencedSymbolsForSymbol(symbol /*node*/, nil, sourceFiles, sourceFilesSet, checker /*, cancellationToken*/, options)) } @@ -981,39 +1055,18 @@ func getReferencedSymbolsForSymbol(originalSymbol *ast.Symbol, node *ast.Node, s // !!! not implemented // state.searchForImportsOfExport(node, symbol, &ExportInfo{exportingModuleSymbol: symbol.Parent, exportKind: ExportKindDefault}) } else { - search := state.createSearch(node, symbol, comingFromUnknown /*comingFrom*/, "", state.populateSearchSymbolSet(symbol, node, options.use == referenceUseRename, options.useAliasesForRename, options.implementations)) + search := state.createSearch(node, symbol, ImpExpKindUnknown /*comingFrom*/, "", state.populateSearchSymbolSet(symbol, node, options.use == referenceUseRename, options.useAliasesForRename, options.implementations)) state.getReferencesInContainerOrFiles(symbol, search) } return state.result } -type ExportKind int - -const ( - ExportKindDefault ExportKind = 0 - ExportKindNamed ExportKind = 1 - ExportKindExportEquals ExportKind = 2 -) - -type ExportInfo struct { - exportingModuleSymbol *ast.Symbol - exportKind ExportKind -} - -type comingFromType int - -const ( - comingFromUnknown comingFromType = 0 - comingFromImport comingFromType = 1 - comingFromExport comingFromType = 2 -) - // Symbol that is currently being searched for. // This will be replaced if we find an alias for the symbol. type refSearch struct { // If coming from an export, we will not recursively search for the imported symbol (since that's where we came from). - comingFrom comingFromType // import, export + comingFrom ImpExpKind // import, export symbol *ast.Symbol text string @@ -1029,21 +1082,10 @@ type refSearch struct { includes func(symbol *ast.Symbol) bool } -// type ( -// ImportTracker = func(exportSymbol *ast.Symbol, exportInfo ExportInfo, isForRename bool) ImportsResult -// ImportsResult struct { -// importSearches []struct { -// importLocation *ast.ModuleExportName -// importSymbol *ast.Symbol -// } -// singleReferences []*ast.Node // ientifier | stringliteral -// indirectUsers []*ast.SourceFile -// } -// ) - -type inheritKey struct { - symbol, parent ast.SymbolId -} +// type inheritKey struct { +// symbol *ast.Symbol +// parent *ast.Symbol +// } type refState struct { sourceFiles []*ast.SourceFile @@ -1054,81 +1096,83 @@ type refState struct { searchMeaning ast.SemanticMeaning options refOptions result []*SymbolAndEntries - - inheritsFromCache map[inheritKey]bool - seenContainingTypeReferences *collections.Set[*ast.Node] // node seen tracker + // inheritsFromCache map[inheritKey]bool + seenContainingTypeReferences collections.Set[*ast.Node] // node seen tracker // seenReExportRHS *collections.Set[*ast.Node] // node seen tracker - // importTracker ImportTracker - symbolIdToReferences map[ast.SymbolId]*SymbolAndEntries - sourceFileToSeenSymbols map[ast.NodeId]*collections.Set[ast.SymbolId] + importTracker ImportTracker + symbolToReferences map[*ast.Symbol]*SymbolAndEntries + sourceFileToSeenSymbols map[*ast.SourceFile]*collections.Set[*ast.Symbol] } func newState(sourceFiles []*ast.SourceFile, sourceFilesSet *collections.Set[string], node *ast.Node, checker *checker.Checker, searchMeaning ast.SemanticMeaning, options refOptions) *refState { return &refState{ - sourceFiles: sourceFiles, - sourceFilesSet: sourceFilesSet, - specialSearchKind: "none", // !!! other search kinds not implemented - checker: checker, - searchMeaning: searchMeaning, - options: options, - result: []*SymbolAndEntries{}, - inheritsFromCache: map[inheritKey]bool{}, - seenContainingTypeReferences: &collections.Set[*ast.Node]{}, + sourceFiles: sourceFiles, + sourceFilesSet: sourceFilesSet, + specialSearchKind: "none", // !!! other search kinds not implemented + checker: checker, + searchMeaning: searchMeaning, + options: options, + // inheritsFromCache: map[inheritKey]bool{}, // seenReExportRHS: &collections.Set[*ast.Node]{}, - symbolIdToReferences: map[ast.SymbolId]*SymbolAndEntries{}, - sourceFileToSeenSymbols: map[ast.NodeId]*collections.Set[ast.SymbolId]{}, + symbolToReferences: map[*ast.Symbol]*SymbolAndEntries{}, + sourceFileToSeenSymbols: map[*ast.SourceFile]*collections.Set[*ast.Symbol]{}, } } +func (state *refState) includesSourceFile(sourceFile *ast.SourceFile) bool { + return state.sourceFilesSet.Has(sourceFile.FileName()) +} + +func (state *refState) getImportSearches(exportSymbol *ast.Symbol, exportInfo *ExportInfo) *ImportsResult { + if state.importTracker == nil { + state.importTracker = createImportTracker(state.sourceFiles, state.sourceFilesSet, state.checker) + } + return state.importTracker(exportSymbol, exportInfo, state.options.use == referenceUseRename) +} + // @param allSearchSymbols set of additional symbols for use by `includes` -func (state *refState) createSearch(location *ast.Node, symbol *ast.Symbol, comingFrom comingFromType, text string, allSearchSymbols []*ast.Symbol) *refSearch { +func (state *refState) createSearch(location *ast.Node, symbol *ast.Symbol, comingFrom ImpExpKind, text string, allSearchSymbols []*ast.Symbol) *refSearch { // Note: if this is an external module symbol, the name doesn't include quotes. // Note: getLocalSymbolForExportDefault handles `export default class C {}`, but not `export default C` or `export { C as default }`. // The other two forms seem to be handled downstream (e.g. in `skipPastExportOrImportSpecifier`), so special-casing the first form // here appears to be intentional). - - symbolToSearchFor := binder.GetLocalSymbolForExportDefault(symbol) - if symbolToSearchFor == nil { - if s := getNonModuleSymbolOfMergedModuleSymbol(symbol); s != nil { - symbolToSearchFor = s - } else { - symbolToSearchFor = symbol + if text == "" { + s := binder.GetLocalSymbolForExportDefault(symbol) + if s == nil { + s = getNonModuleSymbolOfMergedModuleSymbol(symbol) + if s == nil { + s = symbol + } } + text = stringutil.StripQuotes(ast.SymbolName(s)) } - text = func() string { - var name string = ast.SymbolName(symbolToSearchFor) - firstChar, _ := utf8.DecodeRuneInString(name) - lastChar, _ := utf8.DecodeLastRuneInString(name) - if firstChar == lastChar && (firstChar == '\'' || firstChar == '"' || firstChar == '`') { - return name[1 : len(name)-1] - } - return name - }() - escapedText := text if len(allSearchSymbols) == 0 { allSearchSymbols = []*ast.Symbol{symbol} } - includes := func(sym *ast.Symbol) bool { return slices.Contains(allSearchSymbols, sym) } - - search := &refSearch{symbol: symbol, comingFrom: comingFrom, text: text, escapedText: escapedText, allSearchSymbols: allSearchSymbols, includes: includes} + search := &refSearch{ + symbol: symbol, + comingFrom: comingFrom, + text: text, + escapedText: text, + allSearchSymbols: allSearchSymbols, + includes: func(sym *ast.Symbol) bool { return slices.Contains(allSearchSymbols, sym) }, + } if state.options.implementations && location != nil { search.parents = getParentSymbolsOfPropertyAccess(location, symbol, state.checker) } - return search } func (state *refState) referenceAdder(searchSymbol *ast.Symbol) func(*ast.Node, entryKind) { // !!! after find all references is fully implemented, rename this to something like 'getReferenceAdder' - symbolId := ast.GetSymbolId(searchSymbol) - symbolAndEntry := state.symbolIdToReferences[symbolId] - if symbolAndEntry == nil { - state.symbolIdToReferences[symbolId] = NewSymbolAndEntries(definitionKindSymbol, nil, searchSymbol, []*referenceEntry{}) - state.result = append(state.result, state.symbolIdToReferences[symbolId]) - symbolAndEntry = state.symbolIdToReferences[symbolId] + symbolAndEntries := state.symbolToReferences[searchSymbol] + if symbolAndEntries == nil { + symbolAndEntries = NewSymbolAndEntries(definitionKindSymbol, nil, searchSymbol, nil) + state.symbolToReferences[searchSymbol] = symbolAndEntries + state.result = append(state.result, symbolAndEntries) } return func(node *ast.Node, kind entryKind) { - symbolAndEntry.references = append(symbolAndEntry.references, newNodeEntryWithKind(node, kind)) + symbolAndEntries.references = append(symbolAndEntries.references, newNodeEntryWithKind(node, kind)) } } @@ -1211,7 +1255,6 @@ func (state *refState) addImplementationReferences(refNode *ast.Node, addRef fun func (state *refState) getReferencesInContainerOrFiles(symbol *ast.Symbol, search *refSearch) { // Try to get the smallest valid scope that we can limit our search to; // otherwise we'll need to search globally (i.e. include each file). - if scope := getSymbolScope(symbol); scope != nil { state.getReferencesInContainer(scope, ast.GetSourceFileOfNode(scope), search /*addReferencesHere*/, !(scope.Kind == ast.KindSourceFile && !slices.Contains(state.sourceFiles, scope.AsSourceFile()))) } else { @@ -1242,21 +1285,16 @@ func (state *refState) getReferencesInContainer(container *ast.Node, sourceFile } func (state *refState) markSearchedSymbols(sourceFile *ast.SourceFile, symbols []*ast.Symbol) bool { - sourceId := ast.GetNodeId(sourceFile.AsNode()) - seenSymbols := state.sourceFileToSeenSymbols[sourceId] + seenSymbols := state.sourceFileToSeenSymbols[sourceFile] if seenSymbols == nil { - seenSymbols = &collections.Set[ast.SymbolId]{} - state.sourceFileToSeenSymbols[sourceId] = seenSymbols + seenSymbols = &collections.Set[*ast.Symbol]{} + state.sourceFileToSeenSymbols[sourceFile] = seenSymbols } - anyNewSymbols := false for _, sym := range symbols { - symbolId := ast.GetSymbolId(sym) - if seenSymbols.Has(symbolId) { - continue + if seenSymbols.AddIfAbsent(sym) { + anyNewSymbols = true } - anyNewSymbols = true - seenSymbols.Add(symbolId) } return anyNewSymbols } @@ -1333,8 +1371,87 @@ func (state *refState) getReferencesAtLocation(sourceFile *ast.SourceFile, posit } } - // !!! not implemented - // state.getImportOrExportReferences(referenceLocation, referenceSymbol, search) + state.getImportOrExportReferences(referenceLocation, referenceSymbol, search) +} + +func (state *refState) getImportOrExportReferences(referenceLocation *ast.Node, referenceSymbol *ast.Symbol, search *refSearch) { + importOrExport := getImportOrExportSymbol(referenceLocation, referenceSymbol, state.checker, search.comingFrom == ImpExpKindExport) + if importOrExport == nil { + return + } + if importOrExport.kind == ImpExpKindImport { + if !isForRenameWithPrefixAndSuffixText(state.options) { + state.searchForImportedSymbol(importOrExport.symbol) + } + } else { + state.searchForImportsOfExport(referenceLocation, importOrExport.symbol, importOrExport.exportInfo) + } +} + +// Go to the symbol we imported from and find references for it. +func (state *refState) searchForImportedSymbol(symbol *ast.Symbol) { + for _, declaration := range symbol.Declarations { + exportingFile := ast.GetSourceFileOfNode(declaration) + // Need to search in the file even if it's not in the search-file set, because it might export the symbol. + state.getReferencesInSourceFile(exportingFile, state.createSearch(declaration, symbol, ImpExpKindImport, "", nil), state.includesSourceFile(exportingFile)) + } +} + +// Search for all imports of a given exported symbol using `State.getImportSearches`. */ +func (state *refState) searchForImportsOfExport(exportLocation *ast.Node, exportSymbol *ast.Symbol, exportInfo *ExportInfo) { + r := state.getImportSearches(exportSymbol, exportInfo) + + // For `import { foo as bar }` just add the reference to `foo`, and don't otherwise search in the file. + if len(r.singleReferences) != 0 { + addRef := state.referenceAdder(exportSymbol) + for _, singleRef := range r.singleReferences { + if state.shouldAddSingleReference(singleRef) { + addRef(singleRef, entryKindNode) + } + } + } + + // For each import, find all references to that import in its source file. + for _, i := range r.importSearches { + state.getReferencesInSourceFile(ast.GetSourceFileOfNode(i.importLocation), state.createSearch(i.importLocation, i.importSymbol, ImpExpKindExport, "", nil), true /*addReferencesHere*/) + } + + if len(r.indirectUsers) != 0 { + var indirectSearch *refSearch + switch exportInfo.exportKind { + case ExportKindNamed: + indirectSearch = state.createSearch(exportLocation, exportSymbol, ImpExpKindExport, "", nil) + case ExportKindDefault: + // Search for a property access to '.default'. This can't be renamed. + if state.options.use != referenceUseRename { + indirectSearch = state.createSearch(exportLocation, exportSymbol, ImpExpKindExport, "default", nil) + } + } + if indirectSearch != nil { + for _, indirectUser := range r.indirectUsers { + state.searchForName(indirectUser, indirectSearch) + } + } + } +} + +func (state *refState) shouldAddSingleReference(singleRef *ast.Node) bool { + if !state.hasMatchingMeaning(singleRef) { + return false + } + if state.options.use != referenceUseRename { + return true + } + // Don't rename an import type `import("./module-name")` when renaming `name` in `export = name;` + if !ast.IsIdentifier(singleRef) && !ast.IsImportOrExportSpecifier(singleRef.Parent) { + return false + } + // At `default` in `import { default as x }` or `export { default as x }`, do add a reference, but do not rename. + return !(ast.IsImportOrExportSpecifier(singleRef.Parent) && ast.ModuleExportNameIsDefault(singleRef)) +} + +func (state *refState) hasMatchingMeaning(referenceLocation *ast.Node) bool { + return getMeaningFromLocation(referenceLocation)&state.searchMeaning != 0 } func (state *refState) getReferenceForShorthandProperty(referenceSymbol *ast.Symbol, search *refSearch) { @@ -1365,16 +1482,15 @@ func (state *refState) populateSearchSymbolSet(symbol *ast.Symbol, location *ast location, isForRename, !(isForRename && providePrefixAndSuffixText), - func(sym *ast.Symbol, root *ast.Symbol, base *ast.Symbol, _ entryKind) (*ast.Symbol, entryKind) { + func(sym *ast.Symbol, root *ast.Symbol, base *ast.Symbol) *ast.Symbol { // static method/property and instance method/property might have the same name. Only include static or only include instance. if base != nil { if isStaticSymbol(symbol) != isStaticSymbol(base) { base = nil } } - - result = append(result, core.CoalesceList(base, root, sym)) - return nil, entryKindNone + result = append(result, core.OrElse(base, core.OrElse(root, sym))) + return nil }, // when try to find implementation, implementations is true, and not allowed to find base class /*allowBaseTypes*/ func(_ *ast.Symbol) bool { return !implementations }, ) @@ -1387,7 +1503,7 @@ func (state *refState) getRelatedSymbol(search *refSearch, referenceSymbol *ast. referenceLocation, false, /*isForRenamePopulateSearchSymbolSet*/ state.options.use != referenceUseRename || state.options.useAliasesForRename, /*onlyIncludeBindingElementAtReferenceLocation*/ - func(sym *ast.Symbol, rootSymbol *ast.Symbol, baseSymbol *ast.Symbol, kind entryKind) (*ast.Symbol, entryKind) { + func(sym *ast.Symbol, rootSymbol *ast.Symbol, baseSymbol *ast.Symbol) *ast.Symbol { // check whether the symbol used to search itself is just the searched one. if baseSymbol != nil { // static method/property and instance method/property might have the same name. Only check static or only check instance. @@ -1395,15 +1511,15 @@ func (state *refState) getRelatedSymbol(search *refSearch, referenceSymbol *ast. baseSymbol = nil } } - searchSym := core.CoalesceList(baseSymbol, rootSymbol, sym) + searchSym := core.Coalesce(baseSymbol, core.Coalesce(rootSymbol, sym)) if searchSym != nil && search.includes(searchSym) { if rootSymbol != nil && sym.CheckFlags&ast.CheckFlagsSynthetic == 0 { - return rootSymbol, kind + return rootSymbol } - return sym, kind + return sym } // For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol. - return nil, entryKindNone + return nil }, func(rootSymbol *ast.Symbol) bool { return !(len(search.parents) != 0 && !core.Some(search.parents, func(parent *ast.Symbol) bool { @@ -1420,48 +1536,86 @@ func (state *refState) forEachRelatedSymbol( location *ast.Node, isForRenamePopulateSearchSymbolSet, onlyIncludeBindingElementAtReferenceLocation bool, - cbSymbol func(*ast.Symbol, *ast.Symbol, *ast.Symbol, entryKind) (*ast.Symbol, entryKind), + cbSymbol func(*ast.Symbol, *ast.Symbol, *ast.Symbol) *ast.Symbol, allowBaseTypes func(*ast.Symbol) bool, ) (*ast.Symbol, entryKind) { - fromRoot := func(sym *ast.Symbol, kind entryKind) (*ast.Symbol, entryKind) { + fromRoot := func(sym *ast.Symbol) *ast.Symbol { // If this is a union property: // - In populateSearchSymbolsSet we will add all the symbols from all its source symbols in all unioned types. // - In findRelatedSymbol, we will just use the union symbol if any source symbol is included in the search. // If the symbol is an instantiation from a another symbol (e.g. widened symbol): // - In populateSearchSymbolsSet, add the root the list // - In findRelatedSymbol, return the source symbol if that is in the search. (Do not return the instantiation symbol.) - returnKind := entryKindNone - return core.FirstNonNil(state.checker.GetRootSymbols(sym), func(rootSymbol *ast.Symbol) *ast.Symbol { - if s, currKind := cbSymbol(sym, rootSymbol, nil /*baseSymbol*/, kind); s != nil { - returnKind = currKind - return s + for _, rootSymbol := range state.checker.GetRootSymbols(sym) { + if result := cbSymbol(sym, rootSymbol, nil /*baseSymbol*/); result != nil { + return result } // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions if rootSymbol.Parent != nil && rootSymbol.Parent.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 && allowBaseTypes(rootSymbol) { - return getPropertySymbolsFromBaseTypes(rootSymbol.Parent, rootSymbol.Name, state.checker, func(base *ast.Symbol) *ast.Symbol { - s, currKind := cbSymbol(sym, rootSymbol, base, kind) - if s != nil { - returnKind = currKind - } - return s + result := getPropertySymbolsFromBaseTypes(rootSymbol.Parent, rootSymbol.Name, state.checker, func(base *ast.Symbol) *ast.Symbol { + return cbSymbol(sym, rootSymbol, base) }) + if result != nil { + return result + } } - return nil - }), returnKind + } + return nil + } + + if containingObjectLiteralElement := getContainingObjectLiteralElement(location); containingObjectLiteralElement != nil { + /* Because in short-hand property assignment, location has two meaning : property name and as value of the property + * When we do findAllReference at the position of the short-hand property assignment, we would want to have references to position of + * property name and variable declaration of the identifier. + * Like in below example, when querying for all references for an identifier 'name', of the property assignment, the language service + * should show both 'name' in 'obj' and 'name' in variable declaration + * const name = "Foo"; + * const obj = { name }; + * In order to do that, we will populate the search set with the value symbol of the identifier as a value of the property assignment + * so that when matching with potential reference symbol, both symbols from property declaration and variable declaration + * will be included correctly. + */ + shorthandValueSymbol := state.checker.GetShorthandAssignmentValueSymbol(location.Parent) + // gets the local symbol + if shorthandValueSymbol != nil && isForRenamePopulateSearchSymbolSet { + // When renaming 'x' in `const o = { x }`, just rename the local variable, not the property. + return cbSymbol(shorthandValueSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/), entryKindSearchedLocalFoundProperty + } + // If the location is in a context sensitive location (i.e. in an object literal) try + // to get a contextual type for it, and add the property symbol from the contextual + // type to the search set + if contextualType := state.checker.GetContextualType(containingObjectLiteralElement.Parent, checker.ContextFlagsNone); contextualType != nil { + symbols := state.checker.GetPropertySymbolsFromContextualType(containingObjectLiteralElement, contextualType, true /*unionSymbolOk*/) + for _, sym := range symbols { + if res := fromRoot(sym); res != nil { + return res, entryKindSearchedPropertyFoundLocal + } + } + } + // If the location is name of property symbol from object literal destructuring pattern + // Search the property symbol + // for ( { property: p2 } of elems) { } + if propertySymbol := state.checker.GetPropertySymbolOfDestructuringAssignment(location); propertySymbol != nil { + if res := cbSymbol(propertySymbol, nil /*rootSymbol*/, nil /*baseSymbol*/); res != nil { + return res, entryKindSearchedPropertyFoundLocal + } + } + if shorthandValueSymbol != nil { + if res := cbSymbol(shorthandValueSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/); res != nil { + return res, entryKindSearchedLocalFoundProperty + } + } } - // !!! not yet implemented - // const containingObjectLiteralElement = getContainingObjectLiteralElement(location); - // if (containingObjectLiteralElement) {} if aliasedSymbol := getMergedAliasedSymbolOfNamespaceExportDeclaration(location, symbol, state.checker); aliasedSymbol != nil { // In case of UMD module and global merging, search for global as well - if res, kind := cbSymbol(aliasedSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/, entryKindNode); res != nil { - return res, kind + if res := cbSymbol(aliasedSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/); res != nil { + return res, entryKindNode } } - if res, kind := fromRoot(symbol, entryKindNone); res != nil { - return res, core.IfElse(kind != entryKindNone, kind, entryKindNode) + if res := fromRoot(symbol); res != nil { + return res, entryKindNone } if symbol.ValueDeclaration != nil && ast.IsParameterPropertyDeclaration(symbol.ValueDeclaration, symbol.ValueDeclaration.Parent) { @@ -1474,13 +1628,13 @@ func (state *refState) forEachRelatedSymbol( if !(paramProp1.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 && paramProp2.Flags&ast.SymbolFlagsProperty != 0) { panic("Expected a parameter and a property") } - return fromRoot(core.IfElse(symbol.Flags&ast.SymbolFlagsFunctionScopedVariable != 0, paramProp2, paramProp1), entryKindNone) + return fromRoot(core.IfElse(symbol.Flags&ast.SymbolFlagsFunctionScopedVariable != 0, paramProp2, paramProp1)), entryKindNone } if exportSpecifier := ast.GetDeclarationOfKind(symbol, ast.KindExportSpecifier); exportSpecifier != nil && (!isForRenamePopulateSearchSymbolSet || exportSpecifier.PropertyName() == nil) { if localSymbol := state.checker.GetExportSpecifierLocalTargetSymbol(exportSpecifier); localSymbol != nil { - if res, kind := cbSymbol(localSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/, entryKindNode); res != nil { - return res, kind + if res := cbSymbol(localSymbol, nil /*rootSymbol*/, nil /*baseSymbol*/); res != nil { + return res, entryKindNode } } } @@ -1500,7 +1654,7 @@ func (state *refState) forEachRelatedSymbol( if bindingElementPropertySymbol == nil { return nil, entryKindNone } - return fromRoot(bindingElementPropertySymbol, entryKindSearchedPropertyFoundLocal) + return fromRoot(bindingElementPropertySymbol), entryKindSearchedPropertyFoundLocal } debug.Assert(isForRenamePopulateSearchSymbolSet) @@ -1511,7 +1665,7 @@ func (state *refState) forEachRelatedSymbol( if includeOriginalSymbolOfBindingElement { if bindingElementPropertySymbol := getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, state.checker); bindingElementPropertySymbol != nil { - return fromRoot(bindingElementPropertySymbol, entryKindSearchedPropertyFoundLocal) + return fromRoot(bindingElementPropertySymbol), entryKindSearchedPropertyFoundLocal } } return nil, entryKindNone diff --git a/internal/ls/importTracker.go b/internal/ls/importTracker.go new file mode 100644 index 0000000000..947fcab400 --- /dev/null +++ b/internal/ls/importTracker.go @@ -0,0 +1,664 @@ +package ls + +import ( + "slices" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/debug" +) + +type ImpExpKind int32 + +const ( + ImpExpKindUnknown ImpExpKind = iota + ImpExpKindImport + ImpExpKindExport +) + +type ExportKind int32 + +const ( + ExportKindDefault ExportKind = iota + ExportKindNamed + ExportKindExportEquals +) + +type ImportExportSymbol struct { + kind ImpExpKind + symbol *ast.Symbol + exportInfo *ExportInfo +} + +type ExportInfo struct { + exportingModuleSymbol *ast.Symbol + exportKind ExportKind +} + +type LocationAndSymbol struct { + importLocation *ast.Node + importSymbol *ast.Symbol +} + +type ImportsResult struct { + importSearches []LocationAndSymbol + singleReferences []*ast.Node + indirectUsers []*ast.SourceFile +} + +type ImportTracker func(exportSymbol *ast.Symbol, exportInfo *ExportInfo, isForRename bool) *ImportsResult + +// Creates the imports map and returns an ImportTracker that uses it. Call this lazily to avoid calling `getDirectImportsMap` unnecessarily. +func createImportTracker(sourceFiles []*ast.SourceFile, sourceFilesSet *collections.Set[string], checker *checker.Checker) ImportTracker { + allDirectImports := getDirectImportsMap(sourceFiles, checker) + return func(exportSymbol *ast.Symbol, exportInfo *ExportInfo, isForRename bool) *ImportsResult { + directImports, indirectUsers := getImportersForExport(sourceFiles, sourceFilesSet, allDirectImports, exportInfo, checker) + importSearches, singleReferences := getSearchesFromDirectImports(directImports, exportSymbol, exportInfo.exportKind, checker, isForRename) + return &ImportsResult{importSearches, singleReferences, indirectUsers} + } +} + +// Returns a map from a module symbol to all import statements that directly reference the module +func getDirectImportsMap(sourceFiles []*ast.SourceFile, checker *checker.Checker) map[*ast.Symbol][]*ast.Node { + result := make(map[*ast.Symbol][]*ast.Node) + for _, sourceFile := range sourceFiles { + // !!! cancellation + forEachImport(sourceFile, func(importDecl *ast.Node, moduleSpecifier *ast.Node) { + if moduleSymbol := checker.GetSymbolAtLocation(moduleSpecifier); moduleSymbol != nil { + result[moduleSymbol] = append(result[moduleSymbol], importDecl) + } + }) + } + return result +} + +// Calls `action` for each import, re-export, or require() in a file +func forEachImport(sourceFile *ast.SourceFile, action func(importStatement *ast.Node, imported *ast.Node)) { + if sourceFile.ExternalModuleIndicator != nil || len(sourceFile.Imports()) != 0 { + for _, i := range sourceFile.Imports() { + action(importFromModuleSpecifier(i), i) + } + } else { + forEachPossibleImportOrExportStatement(sourceFile.AsNode(), func(node *ast.Node) bool { + switch node.Kind { + case ast.KindExportDeclaration, ast.KindImportDeclaration, ast.KindJSImportDeclaration: + if specifier := node.ModuleSpecifier(); specifier != nil && ast.IsStringLiteral(specifier) { + action(node, specifier) + } + case ast.KindImportEqualsDeclaration: + if isExternalModuleImportEquals(node) { + action(node, node.AsImportEqualsDeclaration().ModuleReference.Expression()) + } + } + return false + }) + } +} + +func forEachPossibleImportOrExportStatement(sourceFileLike *ast.Node, action func(statement *ast.Node) bool) bool { + for _, statement := range getStatementsOfSourceFileLike(sourceFileLike) { + if action(statement) || isAmbientModuleDeclaration(statement) && forEachPossibleImportOrExportStatement(statement, action) { + return true + } + } + return false +} + +func getSourceFileLikeForImportDeclaration(node *ast.Node) *ast.Node { + if ast.IsCallExpression(node) || ast.IsJSDocImportTag(node) { + return ast.GetSourceFileOfNode(node).AsNode() + } + parent := node.Parent + if ast.IsSourceFile(parent) { + return parent + } + debug.Assert(ast.IsModuleBlock(parent) && isAmbientModuleDeclaration(parent.Parent)) + return parent.Parent +} + +func isAmbientModuleDeclaration(node *ast.Node) bool { + return ast.IsModuleDeclaration(node) && ast.IsStringLiteral(node.Name()) +} + +func getStatementsOfSourceFileLike(node *ast.Node) []*ast.Node { + if ast.IsSourceFile(node) { + return node.Statements() + } + if body := node.Body(); body != nil { + return body.Statements() + } + return nil +} + +func getImportersForExport( + sourceFiles []*ast.SourceFile, + sourceFilesSet *collections.Set[string], + allDirectImports map[*ast.Symbol][]*ast.Node, + exportInfo *ExportInfo, + checker *checker.Checker, +) ([]*ast.Node, []*ast.SourceFile) { + var directImports []*ast.Node + var indirectUserDeclarations []*ast.Node + markSeenDirectImport := nodeSeenTracker() + markSeenIndirectUser := nodeSeenTracker() + isAvailableThroughGlobal := exportInfo.exportingModuleSymbol.GlobalExports != nil + + getDirectImports := func(moduleSymbol *ast.Symbol) []*ast.Node { + return allDirectImports[moduleSymbol] + } + + // Adds a module and all of its transitive dependencies as possible indirect users + var addIndirectUser func(*ast.Node, bool) + addIndirectUser = func(sourceFileLike *ast.Node, addTransitiveDependencies bool) { + debug.Assert(!isAvailableThroughGlobal) + if !markSeenIndirectUser(sourceFileLike) { + return + } + indirectUserDeclarations = append(indirectUserDeclarations, sourceFileLike) + if !addTransitiveDependencies { + return + } + moduleSymbol := checker.GetMergedSymbol(sourceFileLike.Symbol()) + if moduleSymbol == nil { + return + } + debug.Assert(moduleSymbol.Flags&ast.SymbolFlagsModule != 0) + for _, directImport := range getDirectImports(moduleSymbol) { + if !ast.IsImportTypeNode(directImport) { + addIndirectUser(getSourceFileLikeForImportDeclaration(directImport), true /*addTransitiveDependencies*/) + } + } + } + + isExported := func(node *ast.Node, stopAtAmbientModule bool) bool { + for node != nil && !(stopAtAmbientModule && isAmbientModuleDeclaration(node)) { + if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) { + return true + } + node = node.Parent + } + return false + } + + handleImportCall := func(importCall *ast.Node) { + top := ast.FindAncestor(importCall, isAmbientModuleDeclaration) + if top == nil { + top = ast.GetSourceFileOfNode(importCall).AsNode() + } + addIndirectUser(top, isExported(importCall, true /*stopAtAmbientModule*/)) + } + + handleNamespaceImport := func(importDeclaration *ast.Node, name *ast.Node, isReExport bool, alreadyAddedDirect bool) { + if exportInfo.exportKind == ExportKindExportEquals { + // This is a direct import, not import-as-namespace. + if !alreadyAddedDirect { + directImports = append(directImports, importDeclaration) + } + } else if !isAvailableThroughGlobal { + sourceFileLike := getSourceFileLikeForImportDeclaration(importDeclaration) + debug.Assert(ast.IsSourceFile(sourceFileLike) || ast.IsModuleDeclaration(sourceFileLike)) + addIndirectUser(sourceFileLike, isReExport || findNamespaceReExports(sourceFileLike, name, checker)) + } + } + + var handleDirectImports func(*ast.Symbol) + handleDirectImports = func(exportingModuleSymbol *ast.Symbol) { + theseDirectImports := getDirectImports(exportingModuleSymbol) + for _, direct := range theseDirectImports { + if !markSeenDirectImport(direct) { + continue + } + // !!! cancellation + switch direct.Kind { + case ast.KindCallExpression: + if ast.IsImportCall(direct) { + handleImportCall(direct) + } else if !isAvailableThroughGlobal { + parent := direct.Parent + if exportInfo.exportKind == ExportKindExportEquals && ast.IsVariableDeclaration(parent) { + name := parent.Name() + if ast.IsIdentifier(name) { + directImports = append(directImports, name) + } + } + } + case ast.KindIdentifier: + // Nothing + case ast.KindImportEqualsDeclaration: + handleNamespaceImport(direct, direct.Name(), ast.HasSyntacticModifier(direct, ast.ModifierFlagsExport), false /*alreadyAddedDirect*/) + case ast.KindImportDeclaration, ast.KindJSImportDeclaration, ast.KindJSDocImportTag: + directImports = append(directImports, direct) + if importClause := direct.ImportClause(); importClause != nil { + if namedBindings := importClause.AsImportClause().NamedBindings; namedBindings != nil && ast.IsNamespaceImport(namedBindings) { + handleNamespaceImport(direct, namedBindings.Name(), false /*isReExport*/, true /*alreadyAddedDirect*/) + break + } + } + if !isAvailableThroughGlobal && ast.IsDefaultImport(direct) { + addIndirectUser(getSourceFileLikeForImportDeclaration(direct), false) + // Add a check for indirect uses to handle synthetic default imports + } + case ast.KindExportDeclaration: + exportClause := direct.AsExportDeclaration().ExportClause + if exportClause == nil { + // This is `export * from "foo"`, so imports of this module may import the export too. + handleDirectImports(getContainingModuleSymbol(direct, checker)) + } else if ast.IsNamespaceExport(exportClause) { + // `export * as foo from "foo"` add to indirect uses + addIndirectUser(getSourceFileLikeForImportDeclaration(direct), true /*addTransitiveDependencies*/) + } else { + // This is `export { foo } from "foo"` and creates an alias symbol, so recursive search will get handle re-exports. + directImports = append(directImports, direct) + } + case ast.KindImportType: + // Only check for typeof import('xyz') + if !isAvailableThroughGlobal && direct.AsImportTypeNode().IsTypeOf && direct.AsImportTypeNode().Qualifier == nil && isExported(direct, false) { + addIndirectUser(ast.GetSourceFileOfNode(direct).AsNode(), true /*addTransitiveDependencies*/) + } + directImports = append(directImports, direct) + default: + debug.FailBadSyntaxKind(direct, "Unexpected import kind.") + } + } + } + + getIndirectUsers := func() []*ast.SourceFile { + if isAvailableThroughGlobal { + // It has `export as namespace`, so anything could potentially use it. + return sourceFiles + } + // Module augmentations may use this module's exports without importing it. + for _, decl := range exportInfo.exportingModuleSymbol.Declarations { + if ast.IsExternalModuleAugmentation(decl) && sourceFilesSet.Has(ast.GetSourceFileOfNode(decl).FileName()) { + addIndirectUser(decl, false) + } + } + // This may return duplicates (if there are multiple module declarations in a single source file, all importing the same thing as a namespace), but `State.markSearchedSymbol` will handle that. + return core.Map(indirectUserDeclarations, ast.GetSourceFileOfNode) + } + + handleDirectImports(exportInfo.exportingModuleSymbol) + return directImports, getIndirectUsers() +} + +func getContainingModuleSymbol(importer *ast.Node, checker *checker.Checker) *ast.Symbol { + return checker.GetMergedSymbol(getSourceFileLikeForImportDeclaration(importer).Symbol()) +} + +// Returns 'true' is the namespace 'name' is re-exported from this module, and 'false' if it is only used locally +func findNamespaceReExports(sourceFileLike *ast.Node, name *ast.Node, checker *checker.Checker) bool { + namespaceImportSymbol := checker.GetSymbolAtLocation(name) + return forEachPossibleImportOrExportStatement(sourceFileLike, func(statement *ast.Node) bool { + if !ast.IsExportDeclaration(statement) { + return false + } + exportClause := statement.AsExportDeclaration().ExportClause + moduleSpecifier := statement.AsExportDeclaration().ModuleSpecifier + return moduleSpecifier == nil && exportClause != nil && ast.IsNamedExports(exportClause) && core.Some(exportClause.Elements(), func(element *ast.Node) bool { + return checker.GetExportSpecifierLocalTargetSymbol(element) == namespaceImportSymbol + }) + }) +} + +func getSearchesFromDirectImports( + directImports []*ast.Node, + exportSymbol *ast.Symbol, + exportKind ExportKind, + checker *checker.Checker, + isForRename bool, +) ([]LocationAndSymbol, []*ast.Node) { + var importSearches []LocationAndSymbol + var singleReferences []*ast.Node + + addSearch := func(location *ast.Node, symbol *ast.Symbol) { + importSearches = append(importSearches, LocationAndSymbol{location, symbol}) + } + + isNameMatch := func(name string) bool { + // Use name of "default" even in `export =` case because we may have allowSyntheticDefaultImports + return name == exportSymbol.Name || exportKind != ExportKindNamed && name == ast.InternalSymbolNameDefault + } + + // `import x = require("./x")` or `import * as x from "./x"`. + // An `export =` may be imported by this syntax, so it may be a direct import. + // If it's not a direct import, it will be in `indirectUsers`, so we don't have to do anything here. + handleNamespaceImportLike := func(importName *ast.Node) { + // Don't rename an import that already has a different name than the export. + if exportKind == ExportKindExportEquals && (!isForRename || isNameMatch(importName.Text())) { + addSearch(importName, checker.GetSymbolAtLocation(importName)) + } + } + + searchForNamedImport := func(namedBindings *ast.Node) { + if namedBindings == nil { + return + } + for _, element := range namedBindings.Elements() { + name := element.Name() + propertyName := element.PropertyName() + if !isNameMatch(core.OrElse(propertyName, name).Text()) { + continue + } + if propertyName != nil { + // This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference. + singleReferences = append(singleReferences, propertyName) + // If renaming `{ foo as bar }`, don't touch `bar`, just `foo`. + // But do rename `foo` in ` { default as foo }` if that's the original export name. + if !isForRename || name.Text() == exportSymbol.Name { + // Search locally for `bar`. + addSearch(name, checker.GetSymbolAtLocation(name)) + } + } else { + var localSymbol *ast.Symbol + if ast.IsExportSpecifier(element) && element.PropertyName() != nil { + localSymbol = checker.GetExportSpecifierLocalTargetSymbol(element) + } else { + localSymbol = checker.GetSymbolAtLocation(name) + } + addSearch(name, localSymbol) + } + } + } + + handleImport := func(decl *ast.Node) { + if ast.IsImportEqualsDeclaration(decl) { + if isExternalModuleImportEquals(decl) { + handleNamespaceImportLike(decl.Name()) + } + return + } + if ast.IsIdentifier(decl) { + handleNamespaceImportLike(decl) + return + } + if ast.IsImportTypeNode(decl) { + if qualifier := decl.AsImportTypeNode().Qualifier; qualifier != nil { + firstIdentifier := ast.GetFirstIdentifier(qualifier) + if firstIdentifier.Text() == ast.SymbolName(exportSymbol) { + singleReferences = append(singleReferences, firstIdentifier) + } + } else if exportKind == ExportKindExportEquals { + singleReferences = append(singleReferences, decl.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal) + } + return + } + // Ignore if there's a grammar error + if !ast.IsStringLiteral(decl.ModuleSpecifier()) { + return + } + if ast.IsExportDeclaration(decl) { + if exportClause := decl.AsExportDeclaration().ExportClause; exportClause != nil && ast.IsNamedExports(exportClause) { + searchForNamedImport(exportClause) + } + return + } + if importClause := decl.ImportClause(); importClause != nil { + if namedBindings := importClause.AsImportClause().NamedBindings; namedBindings != nil { + switch namedBindings.Kind { + case ast.KindNamespaceImport: + handleNamespaceImportLike(namedBindings.Name()) + case ast.KindNamedImports: + // 'default' might be accessed as a named import `{ default as foo }`. + if exportKind == ExportKindNamed || exportKind == ExportKindDefault { + searchForNamedImport(namedBindings) + } + } + } + // `export =` might be imported by a default import if `--allowSyntheticDefaultImports` is on, so this handles both ExportKind.Default and ExportKind.ExportEquals. + // If a default import has the same name as the default export, allow to rename it. + // Given `import f` and `export default function f`, we will rename both, but for `import g` we will rename just that. + if name := importClause.Name(); name != nil && (exportKind == ExportKindDefault || exportKind == ExportKindExportEquals) && (!isForRename || name.Text() == symbolNameNoDefault(exportSymbol)) { + defaultImportAlias := checker.GetSymbolAtLocation(name) + addSearch(name, defaultImportAlias) + } + } + } + for _, decl := range directImports { + handleImport(decl) + } + return importSearches, singleReferences +} + +func getImportOrExportSymbol(node *ast.Node, symbol *ast.Symbol, checker *checker.Checker, comingFromExport bool) *ImportExportSymbol { + exportInfo := func(symbol *ast.Symbol, kind ExportKind) *ImportExportSymbol { + if exportInfo := getExportInfo(symbol, kind, checker); exportInfo != nil { + return &ImportExportSymbol{ + kind: ImpExpKindExport, + symbol: symbol, + exportInfo: exportInfo, + } + } + return nil + } + + getExport := func() *ImportExportSymbol { + getExportAssignmentExport := func(ex *ast.Node) *ImportExportSymbol { + // Get the symbol for the `export =` node; its parent is the module it's the export of. + if ex.Symbol().Parent == nil { + return nil + } + exportKind := core.IfElse(ex.AsExportAssignment().IsExportEquals, ExportKindExportEquals, ExportKindDefault) + return &ImportExportSymbol{ + kind: ImpExpKindExport, + symbol: symbol, + exportInfo: &ExportInfo{ + exportingModuleSymbol: ex.Symbol().Parent, + exportKind: exportKind, + }, + } + } + + // Not meant for use with export specifiers or export assignment. + getExportKindForDeclaration := func(node *ast.Node) ExportKind { + if ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) { + return ExportKindDefault + } + return ExportKindNamed + } + + getSpecialPropertyExport := func(node *ast.Node, useLhsSymbol bool) *ImportExportSymbol { + var kind ExportKind + switch ast.GetAssignmentDeclarationKind(node.AsBinaryExpression()) { + case ast.JSDeclarationKindExportsProperty: + kind = ExportKindNamed + case ast.JSDeclarationKindModuleExports: + kind = ExportKindExportEquals + default: + return nil + } + sym := symbol + if useLhsSymbol { + sym = checker.GetSymbolAtLocation(ast.GetElementOrPropertyAccessName(node.AsBinaryExpression().Left)) + } + if sym == nil { + return nil + } + return exportInfo(sym, kind) + } + + parent := node.Parent + grandparent := parent.Parent + if symbol.ExportSymbol != nil { + if ast.IsPropertyAccessExpression(parent) { + // When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use. + // So check that we are at the declaration. + if ast.IsBinaryExpression(grandparent) && slices.Contains(symbol.Declarations, parent) { + return getSpecialPropertyExport(grandparent, false /*useLhsSymbol*/) + } + return nil + } + return exportInfo(symbol.ExportSymbol, getExportKindForDeclaration(parent)) + } else { + exportNode := getExportNode(parent, node) + switch { + case exportNode != nil && ast.HasSyntacticModifier(exportNode, ast.ModifierFlagsExport): + if ast.IsImportEqualsDeclaration(exportNode) && exportNode.AsImportEqualsDeclaration().ModuleReference == node { + // We're at `Y` in `export import X = Y`. This is not the exported symbol, the left-hand-side is. So treat this as an import statement. + if comingFromExport { + return nil + } + lhsSymbol := checker.GetSymbolAtLocation(exportNode.Name()) + return &ImportExportSymbol{ + kind: ImpExpKindImport, + symbol: lhsSymbol, + } + } + return exportInfo(symbol, getExportKindForDeclaration(exportNode)) + case ast.IsNamespaceExport(parent): + return exportInfo(symbol, ExportKindNamed) + case ast.IsExportAssignment(parent): + return getExportAssignmentExport(parent) + case ast.IsExportAssignment(grandparent): + return getExportAssignmentExport(grandparent) + case ast.IsBinaryExpression(parent): + return getSpecialPropertyExport(parent, true /*useLhsSymbol*/) + case ast.IsBinaryExpression(grandparent): + return getSpecialPropertyExport(grandparent, true /*useLhsSymbol*/) + case ast.IsJSDocTypedefTag(parent) || ast.IsJSDocCallbackTag(parent): + return exportInfo(symbol, ExportKindNamed) + } + } + return nil + } + + getImport := func() *ImportExportSymbol { + if !isNodeImport(node) { + return nil + } + // A symbol being imported is always an alias. So get what that aliases to find the local symbol. + importedSymbol := checker.GetImmediateAliasedSymbol(symbol) + if importedSymbol == nil { + return nil + } + // Search on the local symbol in the exporting module, not the exported symbol. + importedSymbol = skipExportSpecifierSymbol(importedSymbol, checker) + // Similarly, skip past the symbol for 'export =' + if importedSymbol.Name == "export=" { + importedSymbol = getExportEqualsLocalSymbol(importedSymbol, checker) + if importedSymbol == nil { + return nil + } + } + // If the import has a different name than the export, do not continue searching. + // If `importedName` is undefined, do continue searching as the export is anonymous. + // (All imports returned from this function will be ignored anyway if we are in rename and this is a not a named export.) + importedName := symbolNameNoDefault(importedSymbol) + if importedName == "" || importedName == ast.InternalSymbolNameDefault || importedName == symbol.Name { + return &ImportExportSymbol{ + kind: ImpExpKindImport, + symbol: importedSymbol, + } + } + return nil + } + + result := getExport() + if result == nil && !comingFromExport { + result = getImport() + } + return result +} + +func getExportInfo(exportSymbol *ast.Symbol, exportKind ExportKind, c *checker.Checker) *ExportInfo { + // Parent can be nil if an `export` is not at the top-level (which is a compile error). + if exportSymbol.Parent != nil { + exportingModuleSymbol := c.GetMergedSymbol(exportSymbol.Parent) + // `export` may appear in a namespace. In that case, just rely on global search. + if checker.IsExternalModuleSymbol(exportingModuleSymbol) { + return &ExportInfo{ + exportingModuleSymbol: exportingModuleSymbol, + exportKind: exportKind, + } + } + } + return nil +} + +// If a reference is a class expression, the exported node would be its parent. +// If a reference is a variable declaration, the exported node would be the variable statement. +func getExportNode(parent *ast.Node, node *ast.Node) *ast.Node { + var declaration *ast.Node + switch { + case ast.IsVariableDeclaration(parent): + declaration = parent + case ast.IsBindingElement(parent): + declaration = ast.WalkUpBindingElementsAndPatterns(parent) + } + if declaration != nil { + if parent.Name() == node && !ast.IsCatchClause(declaration.Parent) && ast.IsVariableStatement(declaration.Parent.Parent) { + return declaration.Parent.Parent + } + return nil + } + return parent +} + +func isNodeImport(node *ast.Node) bool { + parent := node.Parent + switch parent.Kind { + case ast.KindImportEqualsDeclaration: + return parent.Name() == node && isExternalModuleImportEquals(parent) + case ast.KindImportSpecifier: + // For a rename import `{ foo as bar }`, don't search for the imported symbol. Just find local uses of `bar`. + return parent.PropertyName() == nil + case ast.KindImportClause, ast.KindNamespaceImport: + debug.Assert(parent.Name() == node) + return true + case ast.KindBindingElement: + return ast.IsInJSFile(node) && ast.IsVariableDeclarationInitializedToRequire(parent.Parent.Parent) + } + return false +} + +func isExternalModuleImportEquals(node *ast.Node) bool { + moduleReference := node.AsImportEqualsDeclaration().ModuleReference + return ast.IsExternalModuleReference(moduleReference) && moduleReference.Expression().Kind == ast.KindStringLiteral +} + +// If at an export specifier, go to the symbol it refers to. */ +func skipExportSpecifierSymbol(symbol *ast.Symbol, checker *checker.Checker) *ast.Symbol { + // For `export { foo } from './bar", there's nothing to skip, because it does not create a new alias. But `export { foo } does. + for _, declaration := range symbol.Declarations { + switch { + case ast.IsExportSpecifier(declaration) && declaration.PropertyName() == nil && declaration.Parent.Parent.ModuleSpecifier() == nil: + return core.OrElse(checker.GetExportSpecifierLocalTargetSymbol(declaration), symbol) + case ast.IsPropertyAccessExpression(declaration) && ast.IsModuleExportsAccessExpression(declaration.Expression()) && !ast.IsPrivateIdentifier(declaration.Name()): + // Export of form 'module.exports.propName = expr'; + return checker.GetSymbolAtLocation(declaration) + case ast.IsShorthandPropertyAssignment(declaration) && ast.IsBinaryExpression(declaration.Parent.Parent) && ast.GetAssignmentDeclarationKind(declaration.Parent.Parent.AsBinaryExpression()) == ast.JSDeclarationKindModuleExports: + return checker.GetExportSpecifierLocalTargetSymbol(declaration.Name()) + } + } + return symbol +} + +func getExportEqualsLocalSymbol(importedSymbol *ast.Symbol, checker *checker.Checker) *ast.Symbol { + if importedSymbol.Flags&ast.SymbolFlagsAlias != 0 { + return checker.GetImmediateAliasedSymbol(importedSymbol) + } + decl := debug.CheckDefined(importedSymbol.ValueDeclaration) + switch { + case ast.IsExportAssignment(decl): + return decl.Expression().Symbol() + case ast.IsBinaryExpression(decl): + return decl.AsBinaryExpression().Right.Symbol() + case ast.IsSourceFile(decl): + return decl.Symbol() + } + return nil +} + +func symbolNameNoDefault(symbol *ast.Symbol) string { + if symbol.Name != ast.InternalSymbolNameDefault { + return symbol.Name + } + for _, decl := range symbol.Declarations { + name := ast.GetNameOfDeclaration(decl) + if name != nil && ast.IsIdentifier(name) { + return name.Text() + } + } + return "" +} diff --git a/internal/ls/utilities.go b/internal/ls/utilities.go index a1a64ab617..f35b3364d8 100644 --- a/internal/ls/utilities.go +++ b/internal/ls/utilities.go @@ -62,6 +62,14 @@ func IsInString(sourceFile *ast.SourceFile, position int, previousToken *ast.Nod return false } +func importFromModuleSpecifier(node *ast.Node) *ast.Node { + if result := tryGetImportFromModuleSpecifier(node); result != nil { + return result + } + debug.FailBadSyntaxKind(node.Parent) + return nil +} + func tryGetImportFromModuleSpecifier(node *ast.StringLiteralLike) *ast.Node { switch node.Parent.Kind { case ast.KindImportDeclaration, ast.KindJSImportDeclaration, ast.KindExportDeclaration: @@ -1341,23 +1349,19 @@ func getAllSuperTypeNodes(node *ast.Node) []*ast.TypeNode { } func getParentSymbolsOfPropertyAccess(location *ast.Node, symbol *ast.Symbol, ch *checker.Checker) []*ast.Symbol { - propertyAccessExpression := core.IfElse(isRightSideOfPropertyAccess(location), location.Parent, nil) - if propertyAccessExpression == nil { + if !isRightSideOfPropertyAccess(location) { return nil } - - lhsType := ch.GetTypeAtLocation(propertyAccessExpression.Expression()) + lhsType := ch.GetTypeAtLocation(location.Parent.Expression()) if lhsType == nil { return nil } - var possibleSymbols []*checker.Type - if lhsType.Flags() != 0 { + if lhsType.Flags()&checker.TypeFlagsUnionOrIntersection != 0 { possibleSymbols = lhsType.Types() } else if lhsType.Symbol() != symbol.Parent { possibleSymbols = []*checker.Type{lhsType} } - return core.MapNonNil(possibleSymbols, func(t *checker.Type) *ast.Symbol { if t.Symbol() != nil && t.Symbol().Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 { return t.Symbol() @@ -1373,7 +1377,7 @@ func getParentSymbolsOfPropertyAccess(location *ast.Node, symbol *ast.Symbol, ch // // The value of previousIterationSymbol is undefined when the function is first called. func getPropertySymbolsFromBaseTypes(symbol *ast.Symbol, propertyName string, checker *checker.Checker, cb func(base *ast.Symbol) *ast.Symbol) *ast.Symbol { - seen := collections.Set[*ast.Symbol]{} + var seen collections.Set[*ast.Symbol] var recur func(*ast.Symbol) *ast.Symbol recur = func(symbol *ast.Symbol) *ast.Symbol { // Use `addToSeen` to ensure we don't infinitely recurse in this situation: @@ -1383,23 +1387,24 @@ func getPropertySymbolsFromBaseTypes(symbol *ast.Symbol, propertyName string, ch if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) == 0 || !seen.AddIfAbsent(symbol) { return nil } - - return core.FirstNonNil(symbol.Declarations, func(declaration *ast.Declaration) *ast.Symbol { - return core.FirstNonNil(getAllSuperTypeNodes(declaration), func(typeReference *ast.TypeNode) *ast.Symbol { - propertyType := checker.GetTypeAtLocation(typeReference) - if propertyType == nil || propertyType.Symbol() == nil { - return nil - } - propertySymbol := checker.GetPropertyOfType(propertyType, propertyName) - // Visit the typeReference as well to see if it directly or indirectly uses that property - if propertySymbol != nil { - if r := core.FirstNonNil(checker.GetRootSymbols(propertySymbol), cb); r != nil { - return r + for _, declaration := range symbol.Declarations { + for _, typeReference := range getAllSuperTypeNodes(declaration) { + if propertyType := checker.GetTypeAtLocation(typeReference); propertyType != nil && propertyType.Symbol() != nil { + // Visit the typeReference as well to see if it directly or indirectly uses that property + if propertySymbol := checker.GetPropertyOfType(propertyType, propertyName); propertySymbol != nil { + for _, rootSymbol := range checker.GetRootSymbols(propertySymbol) { + if result := cb(rootSymbol); result != nil { + return result + } + } + } + if result := recur(propertyType.Symbol()); result != nil { + return result } } - return recur(propertyType.Symbol()) - }) - }) + } + } + return nil } return recur(symbol) } @@ -1659,3 +1664,38 @@ func getChildrenFromNonJSDocNode(node *ast.Node, sourceFile *ast.SourceFile) []* } return children } + +// Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 } +func getContainingObjectLiteralElement(node *ast.Node) *ast.Node { + element := getContainingObjectLiteralElementWorker(node) + if element != nil && (ast.IsObjectLiteralExpression(element.Parent) || ast.IsJsxAttributes(element.Parent)) { + return element + } + return nil +} + +func getContainingObjectLiteralElementWorker(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindNumericLiteral: + if node.Parent.Kind == ast.KindComputedPropertyName { + if ast.IsObjectLiteralElement(node.Parent.Parent) { + return node.Parent.Parent + } + return nil + } + fallthrough + case ast.KindIdentifier: + if ast.IsObjectLiteralElement(node.Parent) && (node.Parent.Parent.Kind == ast.KindObjectLiteralExpression || node.Parent.Parent.Kind == ast.KindJsxAttributes) && node.Parent.Name() == node { + return node.Parent + } + } + return nil +} + +// Return a function that returns true if the given node has not been seen +func nodeSeenTracker() func(*ast.Node) bool { + var seen collections.Set[*ast.Node] + return func(node *ast.Node) bool { + return seen.AddIfAbsent(node) + } +} diff --git a/internal/lsp/server.go b/internal/lsp/server.go index ed1a15ddb6..5b6198a885 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -458,6 +458,7 @@ var handlers = sync.OnceValue(func() handlerMap { registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentRangeFormattingInfo, (*Server).handleDocumentRangeFormat) registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentOnTypeFormattingInfo, (*Server).handleDocumentOnTypeFormat) registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentDocumentSymbolInfo, (*Server).handleDocumentSymbol) + registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentRenameInfo, (*Server).handleRename) registerRequestHandler(handlers, lsproto.WorkspaceSymbolInfo, (*Server).handleWorkspaceSymbol) registerRequestHandler(handlers, lsproto.CompletionItemResolveInfo, (*Server).handleCompletionItemResolve) @@ -617,6 +618,9 @@ func (s *Server) handleInitialize(ctx context.Context, params *lsproto.Initializ DocumentSymbolProvider: &lsproto.BooleanOrDocumentSymbolOptions{ Boolean: ptrTo(true), }, + RenameProvider: &lsproto.BooleanOrRenameOptions{ + Boolean: ptrTo(true), + }, }, } @@ -792,6 +796,10 @@ func (s *Server) handleDocumentSymbol(ctx context.Context, ls *ls.LanguageServic return ls.ProvideDocumentSymbols(ctx, params.TextDocument.Uri) } +func (s *Server) handleRename(ctx context.Context, ls *ls.LanguageService, params *lsproto.RenameParams) (lsproto.RenameResponse, error) { + return ls.ProvideRename(ctx, params) +} + func (s *Server) Log(msg ...any) { fmt.Fprintln(s.stderr, msg...) } diff --git a/testdata/baselines/reference/fourslash/findAllRef/EsModuleInteropFindAllReferences.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/EsModuleInteropFindAllReferences.baseline.jsonc index 8de4d78376..8496512cb3 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/EsModuleInteropFindAllReferences.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/EsModuleInteropFindAllReferences.baseline.jsonc @@ -16,6 +16,12 @@ // } +// === /b.ts === + +// import a from "a"; +// a.[|x|]; + + // === findAllReferences === @@ -29,4 +35,4 @@ // === /b.ts === // import a from "a"; -// a./*FIND ALL REFS*/x; +// a./*FIND ALL REFS*/[|x|]; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesFilteringMappedTypeProperty.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesFilteringMappedTypeProperty.baseline.jsonc index ee3d6ebd22..53c7ba4ecf 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesFilteringMappedTypeProperty.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesFilteringMappedTypeProperty.baseline.jsonc @@ -2,7 +2,7 @@ // === /findAllReferencesFilteringMappedTypeProperty.ts === // const obj = { /*FIND ALL REFS*/[|a|]: 1, b: 2 }; -// const filtered: { [P in keyof typeof obj as P extends 'b' ? never : P]: 0; } = { a: 0 }; +// const filtered: { [P in keyof typeof obj as P extends 'b' ? never : P]: 0; } = { [|a|]: 0 }; // filtered.[|a|]; @@ -11,9 +11,9 @@ // === findAllReferences === // === /findAllReferencesFilteringMappedTypeProperty.ts === -// const obj = { a: 1, b: 2 }; +// const obj = { [|a|]: 1, b: 2 }; // const filtered: { [P in keyof typeof obj as P extends 'b' ? never : P]: 0; } = { /*FIND ALL REFS*/[|a|]: 0 }; -// filtered.a; +// filtered.[|a|]; @@ -22,5 +22,5 @@ // === /findAllReferencesFilteringMappedTypeProperty.ts === // const obj = { [|a|]: 1, b: 2 }; -// const filtered: { [P in keyof typeof obj as P extends 'b' ? never : P]: 0; } = { a: 0 }; +// const filtered: { [P in keyof typeof obj as P extends 'b' ? never : P]: 0; } = { [|a|]: 0 }; // filtered./*FIND ALL REFS*/[|a|]; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesOfConstructor.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesOfConstructor.baseline.jsonc index 333f9791ee..8884a4f22f 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesOfConstructor.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllReferencesOfConstructor.baseline.jsonc @@ -15,6 +15,34 @@ // new D(); +// === /b.ts === + +// import { [|C|] } from "./a"; +// new [|C|](); + + +// === /c.ts === + +// import { [|C|] } from "./a"; +// class D extends [|C|] { +// constructor() { +// super(); +// super.method(); +// } +// method() { super(); } +// } +// class E implements [|C|] { +// constructor() { super(); } +// } + + +// === /d.ts === + +// import * as a from "./a"; +// new a.[|C|](); +// class d extends a.[|C|] { constructor() { super(); } + + // === findAllReferences === @@ -34,6 +62,34 @@ // new D(); +// === /b.ts === + +// import { [|C|] } from "./a"; +// new [|C|](); + + +// === /c.ts === + +// import { [|C|] } from "./a"; +// class D extends [|C|] { +// constructor() { +// super(); +// super.method(); +// } +// method() { super(); } +// } +// class E implements [|C|] { +// constructor() { super(); } +// } + + +// === /d.ts === + +// import * as a from "./a"; +// new a.[|C|](); +// class d extends a.[|C|] { constructor() { super(); } + + // === findAllReferences === @@ -51,3 +107,31 @@ // new [|C|](); // const D = [|C|]; // new D(); + + +// === /b.ts === + +// import { [|C|] } from "./a"; +// new [|C|](); + + +// === /c.ts === + +// import { [|C|] } from "./a"; +// class D extends [|C|] { +// constructor() { +// super(); +// super.method(); +// } +// method() { super(); } +// } +// class E implements [|C|] { +// constructor() { super(); } +// } + + +// === /d.ts === + +// import * as a from "./a"; +// new a.[|C|](); +// class d extends a.[|C|] { constructor() { super(); } diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression0.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression0.baseline.jsonc index 98c570f582..2d6a77088a 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression0.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression0.baseline.jsonc @@ -6,6 +6,12 @@ // }; +// === /b.ts === + +// import [|A|] = require("./a"); +// [|A|]; + + // === findAllReferences === @@ -16,9 +22,22 @@ // }; +// === /b.ts === + +// import [|A|] = require("./a"); +// [|A|]; + + // === findAllReferences === +// === /a.ts === + +// export = class [|A|] { +// m() { [|A|]; } +// }; + + // === /b.ts === // import /*FIND ALL REFS*/[|A|] = require("./a"); @@ -28,6 +47,13 @@ // === findAllReferences === +// === /a.ts === + +// export = class [|A|] { +// m() { [|A|]; } +// }; + + // === /b.ts === // import [|A|] = require("./a"); diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression2.baseline.jsonc index 44c8c0dacb..f96e62e6e4 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsClassExpression2.baseline.jsonc @@ -4,9 +4,20 @@ // exports./*FIND ALL REFS*/[|A|] = class {}; +// === /b.js === + +// import { [|A|] } from "./a"; +// [|A|]; + + // === findAllReferences === +// === /a.js === + +// exports.[|A|] = class {}; + + // === /b.js === // import { /*FIND ALL REFS*/[|A|] } from "./a"; @@ -16,6 +27,11 @@ // === findAllReferences === +// === /a.js === + +// exports.[|A|] = class {}; + + // === /b.js === // import { [|A|] } from "./a"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsDefaultImport.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsDefaultImport.baseline.jsonc index a14e0b2b34..bb5c61b725 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsDefaultImport.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsDefaultImport.baseline.jsonc @@ -4,9 +4,19 @@ // export default function /*FIND ALL REFS*/[|a|]() {} +// === /b.ts === + +// import [|a|], * as ns from "./a"; + + // === findAllReferences === +// === /a.ts === + +// export default function [|a|]() {} + + // === /b.ts === // import /*FIND ALL REFS*/[|a|], * as ns from "./a"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsExportConstEqualToClass.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsExportConstEqualToClass.baseline.jsonc index 637ca0d766..1906e220e3 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsExportConstEqualToClass.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsExportConstEqualToClass.baseline.jsonc @@ -5,9 +5,20 @@ // export const /*FIND ALL REFS*/[|D|] = C; +// === /b.ts === + +// import { [|D|] } from "./a"; + + // === findAllReferences === +// === /a.ts === + +// class C {} +// export const [|D|] = C; + + // === /b.ts === // import { /*FIND ALL REFS*/[|D|] } from "./a"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties.baseline.jsonc index c4242d85ab..8781689e20 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties.baseline.jsonc @@ -10,7 +10,7 @@ // } // // var x: I = { -// ["prop1"]: function () { }, +// ["[|prop1|]"]: function () { }, // } @@ -28,7 +28,7 @@ // } // // var x: I = { -// ["prop1"]: function () { }, +// ["[|prop1|]"]: function () { }, // } @@ -37,7 +37,12 @@ // === findAllReferences === // === /findAllRefsForComputedProperties.ts === -// --- (line: 6) skipped --- +// interface I { +// ["[|prop1|]"]: () => void; +// } +// +// class C implements I { +// ["[|prop1|]"]: any; // } // // var x: I = { diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties2.baseline.jsonc index 7cd770e8ad..fb96b320e0 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForComputedProperties2.baseline.jsonc @@ -10,7 +10,7 @@ // } // // var x: I = { -// ["42"]: function () { } +// ["[|42|]"]: function () { } // } @@ -28,7 +28,7 @@ // } // // var x: I = { -// ["42"]: function () { } +// ["[|42|]"]: function () { } // } @@ -37,7 +37,12 @@ // === findAllReferences === // === /findAllRefsForComputedProperties2.ts === -// --- (line: 6) skipped --- +// interface I { +// [[|42|]](): void; +// } +// +// class C implements I { +// [[|42|]]: any; // } // // var x: I = { diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport.baseline.jsonc index e9db3b9ec2..ecd32ba103 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport.baseline.jsonc @@ -4,6 +4,12 @@ // export default function /*FIND ALL REFS*/[|f|]() {} +// === /b.ts === + +// import [|g|] from "./a"; +// [|g|](); + + // === findAllReferences === diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport04.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport04.baseline.jsonc index 02d508dade..4d11cf46ab 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport04.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForDefaultExport04.baseline.jsonc @@ -5,6 +5,12 @@ // export default [|a|]; +// === /b.ts === + +// import [|a|] from "./a"; +// [|a|]; + + // === findAllReferences === @@ -14,6 +20,12 @@ // export default /*FIND ALL REFS*/[|a|]; +// === /b.ts === + +// import [|a|] from "./a"; +// [|a|]; + + // === findAllReferences === diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCall.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCall.baseline.jsonc index 0c643ad3a4..373d783fab 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCall.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCall.baseline.jsonc @@ -2,3 +2,16 @@ // === /app.ts === // export function he/*FIND ALL REFS*/[|hello|]() {}; + + +// === /direct-use.ts === + +// async function main() { +// const mod = await import("./app") +// mod.[|hello|](); +// } + + +// === /indirect-use.ts === + +// import("./re-export").then(mod => mod.services.app.[|hello|]()); diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCallType.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCallType.baseline.jsonc index 0c643ad3a4..736676b75a 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCallType.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForImportCallType.baseline.jsonc @@ -2,3 +2,10 @@ // === /app.ts === // export function he/*FIND ALL REFS*/[|hello|]() {}; + + +// === /indirect-use.ts === + +// import type { app } from "./re-export"; +// declare const app: app +// app.[|hello|](); diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForMappedType.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForMappedType.baseline.jsonc index 2400d5635a..c4956c8440 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForMappedType.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsForMappedType.baseline.jsonc @@ -4,5 +4,5 @@ // interface T { /*FIND ALL REFS*/[|a|]: number }; // type U = { [K in keyof T]: string }; // type V = { [K in keyof U]: boolean }; -// const u: U = { a: "" } -// const v: V = { a: true } +// const u: U = { [|a|]: "" } +// const v: V = { [|a|]: true } diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsImportType.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsImportType.baseline.jsonc index ea6cba2ec1..f497fcf9bd 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsImportType.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsImportType.baseline.jsonc @@ -14,6 +14,11 @@ // export type /*FIND ALL REFS*/[|N|] = number; +// === /b.js === + +// type T = import("./a").[|N|]; + + // === findAllReferences === diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag2.baseline.jsonc index 9987febf6d..a2ec533fb8 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag2.baseline.jsonc @@ -1,4 +1,13 @@ // === findAllReferences === +// === /component.js === + +// export default class [|Component|] { +// constructor() { +// this.id_ = Math.random(); +// } +// // --- (line: 5) skipped --- + + // === /player.js === // import [|Component|] from './component.js'; @@ -7,3 +16,15 @@ // * @extends Component/*FIND ALL REFS*/[|Component|] // */ // export class Player extends [|Component|] {} + + +// === /spatial-navigation.js === + +// /** @import Component from './component.js' */ +// +// export class SpatialNavigation { +// /** +// * @param {[|Component|]} component +// */ +// add(component) {} +// } diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag3.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag3.baseline.jsonc index 7bf992e0a5..7ad3975207 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag3.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsJsDocImportTag3.baseline.jsonc @@ -1,4 +1,13 @@ // === findAllReferences === +// === /component.js === + +// export class [|Component|] { +// constructor() { +// this.id_ = Math.random(); +// } +// // --- (line: 5) skipped --- + + // === /player.js === // import { [|Component|] } from './component.js'; @@ -7,3 +16,15 @@ // * @extends Component/*FIND ALL REFS*/[|Component|] // */ // export class Player extends [|Component|] {} + + +// === /spatial-navigation.js === + +// /** @import { Component } from './component.js' */ +// +// export class SpatialNavigation { +// /** +// * @param {[|Component|]} component +// */ +// add(component) {} +// } diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName06.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName06.baseline.jsonc index c7dc40260b..8e498e58b7 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName06.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName06.baseline.jsonc @@ -14,7 +14,7 @@ // for (var { [|property1|]: p1 } of elems) { // } // var p2; -// for ({ property1 : p2 } of elems) { +// for ({ [|property1|] : p2 } of elems) { // } @@ -36,7 +36,7 @@ // for (var { [|property1|]: p1 } of elems) { // } // var p2; -// for ({ property1 : p2 } of elems) { +// for ({ [|property1|] : p2 } of elems) { // } @@ -58,7 +58,7 @@ // for (var { /*FIND ALL REFS*/[|property1|]: p1 } of elems) { // } // var p2; -// for ({ property1 : p2 } of elems) { +// for ({ [|property1|] : p2 } of elems) { // } @@ -67,8 +67,17 @@ // === findAllReferences === // === /findAllRefsObjectBindingElementPropertyName06.ts === -// --- (line: 10) skipped --- -// for (var { property1: p1 } of elems) { +// interface I { +// [|property1|]: number; +// property2: string; +// } +// +// var elems: I[]; +// for (let { [|property1|]: p } of elems) { +// } +// for (let { [|property1|] } of elems) { +// } +// for (var { [|property1|]: p1 } of elems) { // } // var p2; // for ({ /*FIND ALL REFS*/[|property1|] : p2 } of elems) { @@ -93,5 +102,5 @@ // for (var { [|property1|]: p1 } of elems) { // } // var p2; -// for ({ property1 : p2 } of elems) { +// for ({ [|property1|] : p2 } of elems) { // } diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName07.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName07.baseline.jsonc index 3eb936bfc5..1a23173a81 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName07.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsObjectBindingElementPropertyName07.baseline.jsonc @@ -3,4 +3,4 @@ // let p, b; // -// p, [{ /*FIND ALL REFS*/[|a|]: p, b }] = [{ a: 10, b: true }]; +// p, [{ /*FIND ALL REFS*/[|a|]: p, b }] = [{ [|a|]: 10, b: true }]; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsOnImportAliases.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsOnImportAliases.baseline.jsonc index 960cfac642..d775aa9da9 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsOnImportAliases.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsOnImportAliases.baseline.jsonc @@ -5,9 +5,22 @@ // } +// === /b.ts === + +// import { [|Class|] } from "./a"; +// +// var c = new [|Class|](); + + // === findAllReferences === +// === /a.ts === + +// export class [|Class|] { +// } + + // === /b.ts === // import { /*FIND ALL REFS*/[|Class|] } from "./a"; @@ -18,6 +31,12 @@ // === findAllReferences === +// === /a.ts === + +// export class [|Class|] { +// } + + // === /b.ts === // import { [|Class|] } from "./a"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsPropertyContextuallyTypedByTypeParam01.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsPropertyContextuallyTypedByTypeParam01.baseline.jsonc index 37f4a74f9b..82414b2b25 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsPropertyContextuallyTypedByTypeParam01.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsPropertyContextuallyTypedByTypeParam01.baseline.jsonc @@ -7,10 +7,13 @@ // class C { // method() { // var x: T = { -// a: "" +// [|a|]: "" // }; // x.[|a|]; // } // } // -// // --- (line: 13) skipped --- +// +// var x: IFoo = { +// [|a|]: "ss" +// }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsRedeclaredPropertyInDerivedInterface.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsRedeclaredPropertyInDerivedInterface.baseline.jsonc index 069893a1f3..33f988fd9a 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsRedeclaredPropertyInDerivedInterface.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsRedeclaredPropertyInDerivedInterface.baseline.jsonc @@ -7,8 +7,8 @@ // interface B extends A { // readonly [|x|]: number; // } -// const a: A = { x: 0 }; -// const b: B = { x: 0 }; +// const a: A = { [|x|]: 0 }; +// const b: B = { [|x|]: 0 }; @@ -22,8 +22,8 @@ // interface B extends A { // readonly /*FIND ALL REFS*/[|x|]: number; // } -// const a: A = { x: 0 }; -// const b: B = { x: 0 }; +// const a: A = { [|x|]: 0 }; +// const b: B = { [|x|]: 0 }; @@ -31,12 +31,14 @@ // === findAllReferences === // === /findAllRefsRedeclaredPropertyInDerivedInterface.ts === -// --- (line: 3) skipped --- +// interface A { +// readonly [|x|]: number | string; +// } // interface B extends A { -// readonly x: number; +// readonly [|x|]: number; // } // const a: A = { /*FIND ALL REFS*/[|x|]: 0 }; -// const b: B = { x: 0 }; +// const b: B = { [|x|]: 0 }; @@ -44,8 +46,11 @@ // === findAllReferences === // === /findAllRefsRedeclaredPropertyInDerivedInterface.ts === -// --- (line: 4) skipped --- -// readonly x: number; +// interface A { +// readonly [|x|]: number | string; +// } +// interface B extends A { +// readonly [|x|]: number; // } -// const a: A = { x: 0 }; +// const a: A = { [|x|]: 0 }; // const b: B = { /*FIND ALL REFS*/[|x|]: 0 }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsUnionProperty.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsUnionProperty.baseline.jsonc index 529079f7a0..7afaaa75e1 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsUnionProperty.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsUnionProperty.baseline.jsonc @@ -5,7 +5,7 @@ // | { /*FIND ALL REFS*/[|type|]: "a", prop: number } // | { [|type|]: "b", prop: string }; // const tt: T = { -// type: "a", +// [|type|]: "a", // prop: 0, // }; // declare const t: T; @@ -25,7 +25,7 @@ // | { [|type|]: "a", prop: number } // | { /*FIND ALL REFS*/[|type|]: "b", prop: string }; // const tt: T = { -// type: "a", +// [|type|]: "a", // prop: 0, // }; // declare const t: T; @@ -45,7 +45,7 @@ // | { [|type|]: "a", prop: number } // | { [|type|]: "b", prop: string }; // const tt: T = { -// type: "a", +// [|type|]: "a", // prop: 0, // }; // declare const t: T; @@ -65,7 +65,7 @@ // | { [|type|]: "a", prop: number } // | { [|type|]: "b", prop: string }; // const tt: T = { -// type: "a", +// [|type|]: "a", // prop: 0, // }; // declare const t: T; @@ -85,7 +85,7 @@ // | { [|type|]: "a", prop: number } // | { [|type|]: "b", prop: string }; // const tt: T = { -// type: "a", +// [|type|]: "a", // prop: 0, // }; // declare const t: T; @@ -102,14 +102,18 @@ // === /findAllRefsUnionProperty.ts === // type T = -// | { type: "a", prop: number } +// | { [|type|]: "a", prop: number } // | { type: "b", prop: string }; // const tt: T = { // /*FIND ALL REFS*/[|type|]: "a", // prop: 0, // }; // declare const t: T; -// // --- (line: 9) skipped --- +// if (t.[|type|] === "a") { +// t.[|type|]; +// } else { +// t.type; +// } @@ -122,8 +126,11 @@ // | { type: "b", [|prop|]: string }; // const tt: T = { // type: "a", -// prop: 0, -// // --- (line: 7) skipped --- +// [|prop|]: 0, +// }; +// declare const t: T; +// if (t.type === "a") { +// // --- (line: 10) skipped --- @@ -136,8 +143,11 @@ // | { type: "b", /*FIND ALL REFS*/[|prop|]: string }; // const tt: T = { // type: "a", -// prop: 0, -// // --- (line: 7) skipped --- +// [|prop|]: 0, +// }; +// declare const t: T; +// if (t.type === "a") { +// // --- (line: 10) skipped --- @@ -146,7 +156,7 @@ // === /findAllRefsUnionProperty.ts === // type T = -// | { type: "a", prop: number } +// | { type: "a", [|prop|]: number } // | { type: "b", prop: string }; // const tt: T = { // type: "a", diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment.baseline.jsonc index a280474c32..77400fcc7d 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment.baseline.jsonc @@ -28,7 +28,7 @@ // var name = "Foo"; // // var obj = { /*FIND ALL REFS*/[|name|] }; -// var obj1 = { name: name }; +// var obj1 = { name: [|name|] }; // obj.[|name|]; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment2.baseline.jsonc index 73ad2e0b11..4120f135f2 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWithShorthandPropertyAssignment2.baseline.jsonc @@ -30,7 +30,7 @@ // var dx = "Foo"; // -// module M { export var dx; } +// module M { export var [|dx|]; } // module M { // var z = 100; // export var y = { /*FIND ALL REFS*/[|dx|], z }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWriteAccess.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWriteAccess.baseline.jsonc index 8254d6314d..300d3ee7c6 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWriteAccess.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefsWriteAccess.baseline.jsonc @@ -6,11 +6,11 @@ // } // // let o: Obj = { -// // --- (line: 6) skipped --- - - -// --- (line: 9) skipped --- -// ['num']: 1 +// [`[|num|]`]: 0 +// }; +// +// o = { +// ['[|num|]']: 1 // }; // // o['[|num|]'] = 2; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_meaningAtLocation.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_meaningAtLocation.baseline.jsonc index 16994892f0..f325d43d5b 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_meaningAtLocation.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_meaningAtLocation.baseline.jsonc @@ -14,6 +14,12 @@ // export const T = 0; +// === /b.ts === + +// const x: import("./a").[|T|] = 0; +// const x: typeof import("./a").T = 0; + + // === findAllReferences === @@ -32,6 +38,12 @@ // export const /*FIND ALL REFS*/[|T|] = 0; +// === /b.ts === + +// const x: import("./a").T = 0; +// const x: typeof import("./a").[|T|] = 0; + + // === findAllReferences === @@ -43,7 +55,7 @@ // === /b.ts === -// const x: import("./a")./*FIND ALL REFS*/T = 0; +// const x: import("./a")./*FIND ALL REFS*/[|T|] = 0; // const x: typeof import("./a").T = 0; @@ -59,4 +71,4 @@ // === /b.ts === // const x: import("./a").T = 0; -// const x: typeof import("./a")./*FIND ALL REFS*/T = 0; +// const x: typeof import("./a")./*FIND ALL REFS*/[|T|] = 0; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_named.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_named.baseline.jsonc index 08f1ef2b2a..7c6dd58ef0 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_named.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindAllRefs_importType_named.baseline.jsonc @@ -14,6 +14,12 @@ // export type U = string; +// === /b.ts === + +// const x: import("./a").[|T|] = 0; +// const x: import("./a").U = 0; + + // === findAllReferences === @@ -32,6 +38,12 @@ // export type /*FIND ALL REFS*/[|U|] = string; +// === /b.ts === + +// const x: import("./a").T = 0; +// const x: import("./a").[|U|] = 0; + + // === findAllReferences === @@ -43,7 +55,7 @@ // === /b.ts === -// const x: import("./a")./*FIND ALL REFS*/T = 0; +// const x: import("./a")./*FIND ALL REFS*/[|T|] = 0; // const x: import("./a").U = 0; @@ -59,4 +71,4 @@ // === /b.ts === // const x: import("./a").T = 0; -// const x: import("./a")./*FIND ALL REFS*/U = 0; +// const x: import("./a")./*FIND ALL REFS*/[|U|] = 0; diff --git a/testdata/baselines/reference/fourslash/findAllRef/FindReferencesJSXTagName.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/FindReferencesJSXTagName.baseline.jsonc index 77eb67746d..7f54fb06bd 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/FindReferencesJSXTagName.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/FindReferencesJSXTagName.baseline.jsonc @@ -1,4 +1,10 @@ // === findAllReferences === +// === /RedditSubmission.ts === + +// export const [|SubmissionComp|] = (submission: SubmissionProps) => +//
; + + // === /index.tsx === // import { /*FIND ALL REFS*/[|SubmissionComp|] } from "./RedditSubmission" @@ -15,3 +21,12 @@ // export const /*FIND ALL REFS*/[|SubmissionComp|] = (submission: SubmissionProps) => //
; + + +// === /index.tsx === + +// import { [|SubmissionComp|] } from "./RedditSubmission" +// function displaySubreddit(subreddit: string) { +// let components = submissions +// .map((value, index) => <[|SubmissionComp|] key={ index } elementPosition= { index } {...value.data} />); +// } diff --git a/testdata/baselines/reference/fourslash/findAllRef/GetOccurrencesIsDefinitionOfExport.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/GetOccurrencesIsDefinitionOfExport.baseline.jsonc index c7a261c082..cd5e4c32f9 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/GetOccurrencesIsDefinitionOfExport.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/GetOccurrencesIsDefinitionOfExport.baseline.jsonc @@ -4,9 +4,20 @@ // export var /*FIND ALL REFS*/[|x|] = 12; +// === /main.ts === + +// import { [|x|] } from "./m"; +// const y = [|x|]; + + // === findAllReferences === +// === /m.ts === + +// export var [|x|] = 12; + + // === /main.ts === // import { /*FIND ALL REFS*/[|x|] } from "./m"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossGlobalProjects.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossGlobalProjects.baseline.jsonc index ee1e425b69..932acf04e3 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossGlobalProjects.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossGlobalProjects.baseline.jsonc @@ -42,7 +42,10 @@ // } // // const ia: I = { -// // --- (line: 12) skipped --- +// [|FA|]() { }, +// FB() { }, +// FC() { }, +// }; @@ -87,7 +90,7 @@ // /*FIND ALL REFS*/[|FB|](); // } // -// const ib: I = { FB() {} }; +// const ib: I = { [|FB|]() {} }; @@ -132,4 +135,4 @@ // /*FIND ALL REFS*/[|FC|](); // } // -// const ic: I = { FC() {} }; +// const ic: I = { [|FC|]() {} }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossModuleProjects.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossModuleProjects.baseline.jsonc index 94bdcc9fce..33e9870ae2 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossModuleProjects.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionAcrossModuleProjects.baseline.jsonc @@ -27,7 +27,13 @@ // === findAllReferences === // === /home/src/workspaces/project/a/index.ts === -// --- (line: 7) skipped --- +// import { NS } from "../b"; +// import { [|I|] } from "../c"; +// +// declare module "../b" { +// export namespace NS { +// export function FA(); +// } // } // // declare module "../c" { @@ -35,7 +41,11 @@ // FA(); // } // } -// // --- (line: 15) skipped --- +// +// const ia: [|I|] = { +// FA: NS.FA, +// FC() { }, +// }; // === /home/src/workspaces/project/c/index.ts === @@ -64,7 +74,10 @@ // } // } // -// // --- (line: 16) skipped --- +// const ia: I = { +// [|FA|]: NS.FA, +// FC() { }, +// }; @@ -98,7 +111,13 @@ // === findAllReferences === // === /home/src/workspaces/project/a2/index.ts === -// --- (line: 7) skipped --- +// import { NS } from "../b"; +// import { [|I|] } from "../c"; +// +// declare module "../b" { +// export namespace NS { +// export function FA(); +// } // } // // declare module "../c" { @@ -106,7 +125,11 @@ // FA(); // } // } -// // --- (line: 15) skipped --- +// +// const ia: [|I|] = { +// FA: NS.FA, +// FC() { }, +// }; // === /home/src/workspaces/project/c/index.ts === @@ -135,7 +158,10 @@ // } // } // -// // --- (line: 16) skipped --- +// const ia: I = { +// [|FA|]: NS.FA, +// FC() { }, +// }; @@ -180,7 +206,7 @@ // /*FIND ALL REFS*/[|FB|](); // } // -// const ib: I = { FB() {} }; +// const ib: I = { [|FB|]() {} }; @@ -225,4 +251,4 @@ // /*FIND ALL REFS*/[|FC|](); // } // -// const ic: I = { FC() {} }; +// const ic: I = { [|FC|]() {} }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionShorthandProperty.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionShorthandProperty.baseline.jsonc index a44f1ae070..3bbe7e857d 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionShorthandProperty.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionShorthandProperty.baseline.jsonc @@ -11,7 +11,7 @@ // === /isDefinitionShorthandProperty.ts === // const x = 1; -// const y: { /*FIND ALL REFS*/[|x|]: number } = { x }; +// const y: { /*FIND ALL REFS*/[|x|]: number } = { [|x|] }; @@ -19,5 +19,5 @@ // === findAllReferences === // === /isDefinitionShorthandProperty.ts === -// const x = 1; -// const y: { x: number } = { /*FIND ALL REFS*/[|x|] }; +// const [|x|] = 1; +// const y: { x: number } = { /*FIND ALL REFS*/[|x|]: number } = { [|x|] }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionSingleImport.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionSingleImport.baseline.jsonc index ab6c9a8a01..878b9e0df8 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionSingleImport.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/IsDefinitionSingleImport.baseline.jsonc @@ -4,9 +4,19 @@ // export function /*FIND ALL REFS*/[|f|]() {} +// === /b.ts === + +// import { [|f|] } from "./a"; + + // === findAllReferences === +// === /a.ts === + +// export function [|f|]() {} + + // === /b.ts === // import { /*FIND ALL REFS*/[|f|] } from "./a"; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters.baseline.jsonc index c2fd64a6c6..b373099f0a 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters.baseline.jsonc @@ -9,6 +9,11 @@ // function blah() { return (1 + 2 + container.[|searchProp|]()) === 2; }; +// === /redeclaration.ts === + +// container = { "[|searchProp|]" : 18 }; + + // === /stringIndexer.ts === // function blah2() { container["[|searchProp|]"] }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters2.baseline.jsonc index 3ba7ef9a3f..294e4ea57c 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesBloomFilters2.baseline.jsonc @@ -9,6 +9,11 @@ // function blah() { return (container[[|42|]]) === 2; }; +// === /redeclaration.ts === + +// container = { "[|42|]" : 18 }; + + // === /stringIndexer.ts === // function blah2() { container["[|42|]"] }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedObjectLiteralProperties.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedObjectLiteralProperties.baseline.jsonc index 5d53579bf7..e9ec25e6c9 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedObjectLiteralProperties.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedObjectLiteralProperties.baseline.jsonc @@ -4,5 +4,24 @@ // interface IFoo { /*FIND ALL REFS*/[|xy|]: number; } // // // Assignment -// var a1: IFoo = { xy: 0 }; -// // --- (line: 5) skipped --- +// var a1: IFoo = { [|xy|]: 0 }; +// var a2: IFoo = { [|xy|]: 0 }; +// +// // Function call +// function consumer(f: IFoo) { } +// consumer({ [|xy|]: 1 }); +// +// // Type cast +// var c = { [|xy|]: 0 }; +// +// // Array literal +// var ar: IFoo[] = [{ [|xy|]: 1 }, { [|xy|]: 2 }]; +// +// // Nested object literal +// var ob: { ifoo: IFoo } = { ifoo: { [|xy|]: 0 } }; +// +// // Widened type +// var w: IFoo = { [|xy|]: undefined }; +// +// // Untped -- should not be included +// var u = { xy: 0 }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties.baseline.jsonc index f27d573b69..65e37f9a5f 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties.baseline.jsonc @@ -7,7 +7,33 @@ // } // // interface B { -// // --- (line: 7) skipped --- +// b: number; +// common: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -23,7 +49,28 @@ // } // // // Assignment -// // --- (line: 12) skipped --- +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -31,15 +78,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 8) skipped --- +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; // } // // // Assignment // var v1: A | B = { a: 0, /*FIND ALL REFS*/[|common|]: "" }; -// var v2: A | B = { b: 0, common: 3 }; +// var v2: A | B = { b: 0, [|common|]: 3 }; // // // Function call -// // --- (line: 16) skipped --- +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -47,15 +118,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 9) skipped --- +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } // // // Assignment -// var v1: A | B = { a: 0, common: "" }; +// var v1: A | B = { a: 0, [|common|]: "" }; // var v2: A | B = { b: 0, /*FIND ALL REFS*/[|common|]: 3 }; // // // Function call // function consumer(f: A | B) { } -// // --- (line: 17) skipped --- +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -63,15 +158,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 13) skipped --- +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; // // // Function call // function consumer(f: A | B) { } // consumer({ a: 0, b: 0, /*FIND ALL REFS*/[|common|]: 1 }); // // // Type cast -// var c = { common: 0, b: 0 }; -// // --- (line: 21) skipped --- +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -79,15 +198,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 16) skipped --- -// consumer({ a: 0, b: 0, common: 1 }); +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); // // // Type cast // var c = { /*FIND ALL REFS*/[|common|]: 0, b: 0 }; // // // Array literal -// var ar: Array = [{ a: 0, common: "" }, { b: 0, common: 0 }]; -// // --- (line: 24) skipped --- +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -95,15 +238,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 19) skipped --- -// var c = { common: 0, b: 0 }; +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; // // // Array literal -// var ar: Array = [{ a: 0, /*FIND ALL REFS*/[|common|]: "" }, { b: 0, common: 0 }]; +// var ar: Array = [{ a: 0, /*FIND ALL REFS*/[|common|]: "" }, { b: 0, [|common|]: 0 }]; // // // Nested object literal -// var ob: { aorb: A|B } = { aorb: { b: 0, common: 0 } }; -// // --- (line: 27) skipped --- +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -111,15 +278,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 19) skipped --- -// var c = { common: 0, b: 0 }; +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; // // // Array literal -// var ar: Array = [{ a: 0, common: "" }, { b: 0, /*FIND ALL REFS*/[|common|]: 0 }]; +// var ar: Array = [{ a: 0, common: "" }, { b: 0, /*FIND ALL REFS*/[|common|]: "" }, { b: 0, [|common|]: 0 }]; // // // Nested object literal -// var ob: { aorb: A|B } = { aorb: { b: 0, common: 0 } }; -// // --- (line: 27) skipped --- +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; +// +// // Widened type +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -127,15 +318,39 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 22) skipped --- -// var ar: Array = [{ a: 0, common: "" }, { b: 0, common: 0 }]; +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; // // // Nested object literal // var ob: { aorb: A|B } = { aorb: { b: 0, /*FIND ALL REFS*/[|common|]: 0 } }; // // // Widened type -// var w: A|B = { a:0, common: undefined }; -// // --- (line: 30) skipped --- +// var w: A|B = { a:0, [|common|]: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; @@ -143,8 +358,32 @@ // === findAllReferences === // === /referencesForContextuallyTypedUnionProperties.ts === -// --- (line: 25) skipped --- -// var ob: { aorb: A|B } = { aorb: { b: 0, common: 0 } }; +// interface A { +// a: number; +// [|common|]: string; +// } +// +// interface B { +// b: number; +// [|common|]: number; +// } +// +// // Assignment +// var v1: A | B = { a: 0, [|common|]: "" }; +// var v2: A | B = { b: 0, [|common|]: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, b: 0, [|common|]: 1 }); +// +// // Type cast +// var c = { [|common|]: 0, b: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, [|common|]: "" }, { b: 0, [|common|]: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { b: 0, [|common|]: 0 } }; // // // Widened type // var w: A|B = { a:0, /*FIND ALL REFS*/[|common|]: undefined }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties2.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties2.baseline.jsonc index bbb244491d..fcf8502a8a 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties2.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForContextuallyTypedUnionProperties2.baseline.jsonc @@ -9,4 +9,26 @@ // common: number; // } // -// // --- (line: 11) skipped --- +// // Assignment +// var v1: A | B = { a: 0, common: "" }; +// var v2: A | B = { [|b|]: 0, common: 3 }; +// +// // Function call +// function consumer(f: A | B) { } +// consumer({ a: 0, [|b|]: 0, common: 1 }); +// +// // Type cast +// var c = { common: 0, [|b|]: 0 }; +// +// // Array literal +// var ar: Array = [{ a: 0, common: "" }, { [|b|]: 0, common: 0 }]; +// +// // Nested object literal +// var ob: { aorb: A|B } = { aorb: { [|b|]: 0, common: 0 } }; +// +// // Widened type +// var w: A|B = { [|b|]:undefined, common: undefined }; +// +// // Untped -- should not be included +// var u1 = { a: 0, b: 0, common: "" }; +// var u2 = { b: 0, common: 0 }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForNumericLiteralPropertyNames.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForNumericLiteralPropertyNames.baseline.jsonc index c21f9d1a71..0ecddc1b94 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForNumericLiteralPropertyNames.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForNumericLiteralPropertyNames.baseline.jsonc @@ -7,5 +7,5 @@ // // var x: Foo; // x[[|12|]]; -// x = { "12": 0 }; -// x = { 12: 0 }; +// x = { "[|12|]": 0 }; +// x = { [|12|]: 0 }; diff --git a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForStringLiteralPropertyNames.baseline.jsonc b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForStringLiteralPropertyNames.baseline.jsonc index b4eed576ab..20796674ce 100644 --- a/testdata/baselines/reference/fourslash/findAllRef/ReferencesForStringLiteralPropertyNames.baseline.jsonc +++ b/testdata/baselines/reference/fourslash/findAllRef/ReferencesForStringLiteralPropertyNames.baseline.jsonc @@ -8,5 +8,5 @@ // var x: Foo; // x.[|ss|]; // x["[|ss|]"]; -// x = { "ss": 0 }; -// x = { ss: 0 }; +// x = { "[|ss|]": 0 }; +// x = { [|ss|]: 0 };