Skip to content

Commit 41e1964

Browse files
authored
Fix getReferencedSymbolsForSymbol, implement getReferencesAtExportSpecifier (#2304)
1 parent 9a0b323 commit 41e1964

File tree

43 files changed

+724
-337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+724
-337
lines changed

internal/fourslash/_scripts/failingTests.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ TestQuickinfoWrongComment
539539
TestRecursiveInternalModuleImport
540540
TestReferencesInEmptyFile
541541
TestRegexDetection
542-
TestRenameForAliasingExport02
543542
TestRenameFromNodeModulesDep1
544543
TestRenameFromNodeModulesDep2
545544
TestRenameFromNodeModulesDep3

internal/fourslash/fourslash.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,12 @@ func sendRequest[Params, Resp any](t *testing.T, f *FourslashTest, info lsproto.
616616
if resMsg == nil {
617617
t.Fatalf(prefix+"Nil response received for %s request", info.Method)
618618
}
619+
resp := resMsg.AsResponse()
620+
if resp.Error != nil {
621+
t.Fatalf(prefix+"%s request returned error: %s", info.Method, resp.Error.String())
622+
}
619623
if !resultOk {
620-
t.Fatalf(prefix+"Unexpected %s response type: %T", info.Method, resMsg.AsResponse().Result)
624+
t.Fatalf(prefix+"Unexpected %s response type: %T", info.Method, resp.Result)
621625
}
622626
return result
623627
}

internal/fourslash/tests/gen/renameForAliasingExport02_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
func TestRenameForAliasingExport02(t *testing.T) {
1111
t.Parallel()
12-
t.Skip()
12+
1313
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
1414
const content = `// @Filename: foo.ts
1515
let x = 1;

internal/fourslash/tests/statefindallrefs_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,78 @@ import * as shared from "../../shared/dist"
10411041
}
10421042
}
10431043

1044+
func TestFindAllRefsReExportInMultiProjectSolution(t *testing.T) {
1045+
t.Parallel()
1046+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
1047+
content := `
1048+
// @stateBaseline: true
1049+
// @Filename: /tsconfig.base.json
1050+
{
1051+
"compilerOptions": {
1052+
"rootDir": ".",
1053+
"outDir": "target",
1054+
"module": "ESNext",
1055+
"moduleResolution": "bundler",
1056+
"composite": true,
1057+
"declaration": true,
1058+
"strict": true
1059+
},
1060+
"include": []
1061+
}
1062+
// @Filename: /tsconfig.json
1063+
{
1064+
"extends": "./tsconfig.base.json",
1065+
"references": [
1066+
{ "path": "project-a" },
1067+
{ "path": "project-b" },
1068+
{ "path": "project-c" },
1069+
]
1070+
}
1071+
// @Filename: /project-a/tsconfig.json
1072+
{
1073+
"extends": "../tsconfig.base.json",
1074+
"include": ["*"]
1075+
}
1076+
// @Filename: /project-a/private.ts
1077+
export const /*symbolA*/symbolA = 'some-symbol';
1078+
console.log(symbolA);
1079+
// @Filename: /project-a/public.ts
1080+
export { symbolA } from './private';
1081+
// @Filename: /project-b/tsconfig.json
1082+
{
1083+
"extends": "../tsconfig.base.json",
1084+
"include": ["*"]
1085+
}
1086+
// @Filename: /project-b/public.ts
1087+
export const /*symbolB*/symbolB = 'symbol-b';
1088+
// @Filename: /project-c/tsconfig.json
1089+
{
1090+
"extends": "../tsconfig.base.json",
1091+
"include": ["*"],
1092+
"references": [
1093+
{ "path": "../project-a" },
1094+
{ "path": "../project-b" },
1095+
]
1096+
}
1097+
// @Filename: /project-c/index.ts
1098+
import { symbolB } from '../project-b/public';
1099+
import { /*symbolAUsage*/symbolA } from '../project-a/public';
1100+
console.log(symbolB);
1101+
console.log(symbolA);
1102+
`
1103+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
1104+
defer done()
1105+
1106+
// Find all refs for symbolA - should find definition in private.ts, re-export in public.ts, and usage in project-c/index.ts
1107+
f.VerifyBaselineFindAllReferences(t, "symbolA")
1108+
1109+
// Find all refs for symbolB - should find definition and usage (no re-export involved)
1110+
f.VerifyBaselineFindAllReferences(t, "symbolB")
1111+
1112+
// Find all refs from the usage site - should also work
1113+
f.VerifyBaselineFindAllReferences(t, "symbolAUsage")
1114+
}
1115+
10441116
func TestFindAllRefsDeclarationInOtherProject(t *testing.T) {
10451117
t.Parallel()
10461118
type testCase struct {

internal/ls/findallreferences.go

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,14 +1528,12 @@ func getReferencedSymbolsForSymbol(originalSymbol *ast.Symbol, node *ast.Node, s
15281528
state := newState(sourceFiles, sourceFilesSet, node, checker /*, cancellationToken*/, searchMeaning, options)
15291529

15301530
var exportSpecifier *ast.Node
1531-
if !isForRenameWithPrefixAndSuffixText(options) || len(symbol.Declarations) == 0 {
1531+
if isForRenameWithPrefixAndSuffixText(options) && len(symbol.Declarations) != 0 {
15321532
exportSpecifier = core.Find(symbol.Declarations, ast.IsExportSpecifier)
15331533
}
15341534
if exportSpecifier != nil {
1535-
// !!! not implemented
1536-
15371535
// When renaming at an export specifier, rename the export and not the thing being exported.
1538-
// state.getReferencesAtExportSpecifier(exportSpecifier.Name(), symbol, exportSpecifier.AsExportSpecifier(), state.createSearch(node, originalSymbol, comingFromUnknown /*comingFrom*/, "", nil), true /*addReferencesHere*/, true /*alwaysGetReferences*/)
1536+
state.getReferencesAtExportSpecifier(exportSpecifier.Name(), symbol, exportSpecifier.AsExportSpecifier(), state.createSearch(node, originalSymbol, ImpExpKindUnknown /*comingFrom*/, "", nil), true /*addReferencesHere*/, true /*alwaysGetReferences*/)
15391537
} else if node != nil && node.Kind == ast.KindDefaultKeyword && symbol.Name == ast.InternalSymbolNameDefault && symbol.Parent != nil {
15401538
state.addReference(node, symbol, entryKindNode)
15411539
state.searchForImportsOfExport(node, symbol, &ExportInfo{exportingModuleSymbol: symbol.Parent, exportKind: ExportKindDefault})
@@ -1583,22 +1581,21 @@ type refState struct {
15831581
result []*SymbolAndEntries
15841582
inheritsFromCache map[inheritKey]bool
15851583
seenContainingTypeReferences collections.Set[*ast.Node] // node seen tracker
1586-
// seenReExportRHS *collections.Set[*ast.Node] // node seen tracker
1587-
importTracker ImportTracker
1588-
symbolToReferences map[*ast.Symbol]*SymbolAndEntries
1589-
sourceFileToSeenSymbols map[*ast.SourceFile]*collections.Set[*ast.Symbol]
1584+
seenReExportRHS collections.Set[*ast.Node] // node seen tracker
1585+
importTracker ImportTracker
1586+
symbolToReferences map[*ast.Symbol]*SymbolAndEntries
1587+
sourceFileToSeenSymbols map[*ast.SourceFile]*collections.Set[*ast.Symbol]
15901588
}
15911589

15921590
func newState(sourceFiles []*ast.SourceFile, sourceFilesSet *collections.Set[string], node *ast.Node, checker *checker.Checker, searchMeaning ast.SemanticMeaning, options refOptions) *refState {
15931591
return &refState{
1594-
sourceFiles: sourceFiles,
1595-
sourceFilesSet: sourceFilesSet,
1596-
specialSearchKind: getSpecialSearchKind(node),
1597-
checker: checker,
1598-
searchMeaning: searchMeaning,
1599-
options: options,
1600-
inheritsFromCache: map[inheritKey]bool{},
1601-
// seenReExportRHS: &collections.Set[*ast.Node]{},
1592+
sourceFiles: sourceFiles,
1593+
sourceFilesSet: sourceFilesSet,
1594+
specialSearchKind: getSpecialSearchKind(node),
1595+
checker: checker,
1596+
searchMeaning: searchMeaning,
1597+
options: options,
1598+
inheritsFromCache: map[inheritKey]bool{},
16021599
symbolToReferences: map[*ast.Symbol]*SymbolAndEntries{},
16031600
sourceFileToSeenSymbols: map[*ast.SourceFile]*collections.Set[*ast.Symbol]{},
16041601
}
@@ -1911,9 +1908,7 @@ func (state *refState) getReferencesAtLocation(sourceFile *ast.SourceFile, posit
19111908
}
19121909

19131910
if parent.Kind == ast.KindExportSpecifier {
1914-
// !!! not implemented
1915-
// debug.Assert(referenceLocation.Kind == ast.KindIdentifier || referenceLocation.Kind == ast.KindStringLiteral)
1916-
// state.getReferencesAtExportSpecifier(referenceLocation /* Identifier | StringLiteral*/, referenceSymbol, parent.AsExportSpecifier(), search, addReferencesHere, false /*alwaysGetReferences*/)
1911+
state.getReferencesAtExportSpecifier(referenceLocation, referenceSymbol, parent.AsExportSpecifier(), search, addReferencesHere, false /*alwaysGetReferences*/)
19171912
return
19181913
}
19191914

@@ -2034,6 +2029,82 @@ func (state *refState) getImportOrExportReferences(referenceLocation *ast.Node,
20342029
}
20352030
}
20362031

2032+
func (state *refState) markSeenReExportRHS(node *ast.Node) bool {
2033+
return state.seenReExportRHS.AddIfAbsent(node)
2034+
}
2035+
2036+
func (state *refState) getReferencesAtExportSpecifier(
2037+
referenceLocation *ast.Node,
2038+
referenceSymbol *ast.Symbol,
2039+
exportSpecifier *ast.ExportSpecifier,
2040+
search *refSearch,
2041+
addReferencesHere bool,
2042+
alwaysGetReferences bool,
2043+
) {
2044+
debug.Assert(!alwaysGetReferences || state.options.useAliasesForRename, "If alwaysGetReferences is true, then prefix/suffix text must be enabled")
2045+
2046+
exportDeclaration := exportSpecifier.Parent.Parent.AsExportDeclaration()
2047+
propertyName := exportSpecifier.PropertyName
2048+
name := exportSpecifier.Name()
2049+
localSymbol := getLocalSymbolForExportSpecifier(referenceLocation.AsIdentifier(), referenceSymbol, exportSpecifier, state.checker)
2050+
2051+
if !alwaysGetReferences && !search.includes(localSymbol) {
2052+
return
2053+
}
2054+
2055+
addRef := func() {
2056+
if addReferencesHere {
2057+
state.addReference(referenceLocation, localSymbol, entryKindNode)
2058+
}
2059+
}
2060+
2061+
if propertyName == nil {
2062+
// Don't rename at `export { default } from "m";`. (but do continue to search for imports of the re-export)
2063+
if !(state.options.use == referenceUseRename && ast.ModuleExportNameIsDefault(name)) {
2064+
addRef()
2065+
}
2066+
} else if referenceLocation == propertyName.AsNode() {
2067+
// For `export { foo as bar } from "baz"`, "`foo`" will be added from the singleReferences for import searches of the original export.
2068+
// For `export { foo as bar };`, where `foo` is a local, so add it now.
2069+
if exportDeclaration.ModuleSpecifier == nil {
2070+
addRef()
2071+
}
2072+
2073+
if addReferencesHere && state.options.use != referenceUseRename && state.markSeenReExportRHS(name) {
2074+
exportSymbol := exportSpecifier.AsNode().Symbol()
2075+
debug.Assert(exportSymbol != nil, "exportSpecifier.Symbol() should not be nil")
2076+
state.addReference(name, exportSymbol, entryKindNode)
2077+
}
2078+
} else {
2079+
if state.markSeenReExportRHS(referenceLocation) {
2080+
addRef()
2081+
}
2082+
}
2083+
2084+
// For `export { foo as bar }`, rename `foo`, but not `bar`.
2085+
if !isForRenameWithPrefixAndSuffixText(state.options) || alwaysGetReferences {
2086+
isDefaultExport := ast.ModuleExportNameIsDefault(referenceLocation) || ast.ModuleExportNameIsDefault(exportSpecifier.Name())
2087+
exportKind := ExportKindNamed
2088+
if isDefaultExport {
2089+
exportKind = ExportKindDefault
2090+
}
2091+
exportSymbol := exportSpecifier.AsNode().Symbol()
2092+
debug.Assert(exportSymbol != nil, "exportSpecifier.Symbol() should not be nil")
2093+
exportInfo := getExportInfo(exportSymbol, exportKind, state.checker)
2094+
if exportInfo != nil {
2095+
state.searchForImportsOfExport(referenceLocation, exportSymbol, exportInfo)
2096+
}
2097+
}
2098+
2099+
// At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
2100+
if search.comingFrom != ImpExpKindExport && exportDeclaration.ModuleSpecifier != nil && propertyName == nil && !isForRenameWithPrefixAndSuffixText(state.options) {
2101+
imported := state.checker.GetExportSpecifierLocalTargetSymbol(exportSpecifier.AsNode())
2102+
if imported != nil {
2103+
state.searchForImportedSymbol(imported)
2104+
}
2105+
}
2106+
}
2107+
20372108
// Go to the symbol we imported from and find references for it.
20382109
func (state *refState) searchForImportedSymbol(symbol *ast.Symbol) {
20392110
for _, declaration := range symbol.Declarations {

testdata/baselines/reference/fourslash/documentHighlights/documentHighlightInExport1.baseline.jsonc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// === documentHighlights ===
22
// === /documentHighlightInExport1.ts ===
33
// class /*HIGHLIGHTS*/[|C|] {}
4-
// export { C as D };
4+
// export { [|C|] as [|D|] };
55

66

77

@@ -15,7 +15,7 @@
1515
// === documentHighlights ===
1616
// === /documentHighlightInExport1.ts ===
1717
// class [|C|] {}
18-
// export { /*HIGHLIGHTS*/C as D };
18+
// export { /*HIGHLIGHTS*/[|C|] as [|D|] };
1919

2020

2121

@@ -29,4 +29,4 @@
2929
// === documentHighlights ===
3030
// === /documentHighlightInExport1.ts ===
3131
// class C {}
32-
// export { C as /*HIGHLIGHTS*/D };
32+
// export { C as /*HIGHLIGHTS*/[|D|] };

testdata/baselines/reference/fourslash/documentHighlights/documentHighlightInTypeExport.baseline.jsonc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
// === documentHighlights ===
99
// === /1.ts ===
1010
// type A = 1;
11-
// export { /*HIGHLIGHTS*/A as B };
11+
// export { /*HIGHLIGHTS*/[|A|] as [|B|] };
1212

1313

1414

1515
// === documentHighlights ===
1616
// === /1.ts ===
1717
// type A = 1;
18-
// export { A as /*HIGHLIGHTS*/B };
18+
// export { A as /*HIGHLIGHTS*/[|B|] };
1919

2020

2121

@@ -31,7 +31,7 @@
3131
// === /2.ts ===
3232
// type A = 1;
3333
// let /*HIGHLIGHTS*/[|A|]: A = 1;
34-
// export { A as B };
34+
// export { [|A|] as [|B|] };
3535

3636

3737

@@ -47,15 +47,15 @@
4747
// === /2.ts ===
4848
// type A = 1;
4949
// let [|A|]: A = 1;
50-
// export { /*HIGHLIGHTS*/A as B };
50+
// export { /*HIGHLIGHTS*/[|A|] as [|B|] };
5151

5252

5353

5454
// === documentHighlights ===
5555
// === /2.ts ===
5656
// type A = 1;
5757
// let A: A = 1;
58-
// export { A as /*HIGHLIGHTS*/B };
58+
// export { A as /*HIGHLIGHTS*/[|B|] };
5959

6060

6161

@@ -71,7 +71,7 @@
7171
// === /3.ts ===
7272
// type A = 1;
7373
// let /*HIGHLIGHTS*/[|A|]: A = 1;
74-
// export type { A as B };
74+
// export type { [|A|] as [|B|] };
7575

7676

7777

@@ -87,12 +87,12 @@
8787
// === /3.ts ===
8888
// type A = 1;
8989
// let [|A|]: A = 1;
90-
// export type { /*HIGHLIGHTS*/A as B };
90+
// export type { /*HIGHLIGHTS*/[|A|] as [|B|] };
9191

9292

9393

9494
// === documentHighlights ===
9595
// === /3.ts ===
9696
// type A = 1;
9797
// let A: A = 1;
98-
// export type { A as /*HIGHLIGHTS*/B };
98+
// export type { A as /*HIGHLIGHTS*/[|B|] };
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// === documentHighlights ===
22
// === /documentHighlightsInvalidGlobalThis.ts ===
33
// declare global {
4-
// export { globalThis as /*HIGHLIGHTS*/global }
4+
// export { globalThis as /*HIGHLIGHTS*/[|global|] }
55
// }
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
// === findAllReferences ===
22
// === /bar.ts ===
3-
// import { [|Foo|]/*FIND ALL REFS*/ } from "./foo";
3+
// import { [|Foo|]/*FIND ALL REFS*/ } from "./foo";
4+
5+
// === /foo.ts ===
6+
// export { [|Foo|] }

testdata/baselines/reference/fourslash/findAllReferences/findAllRefsCommonJsRequire.baseline.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// === findAllReferences ===
22
// === /a.js ===
33
// function [|f|]() { }
4-
// export { f }
4+
// export { [|f|] }
55

66
// === /b.js ===
77
// const { [|f|] } = require('./a')

0 commit comments

Comments
 (0)