Skip to content

Commit f0484dd

Browse files
Port go-to-definition baseline tests (#1437)
1 parent b780b19 commit f0484dd

File tree

528 files changed

+595717
-22
lines changed

Some content is hidden

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

528 files changed

+595717
-22
lines changed

internal/fourslash/_scripts/convertFourslash.mts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
150150
case "baselineFindAllReferences":
151151
// `verify.baselineFindAllReferences(...)`
152152
return [parseBaselineFindAllReferencesArgs(callExpression.arguments)];
153+
case "baselineGoToDefinition":
154+
case "baselineGetDefinitionAtPosition":
155+
// Both of these take the same arguments, but differ in that...
156+
// - `verify.baselineGoToDefinition(...)` called getDefinitionAndBoundSpan
157+
// - `verify.baselineGetDefinitionAtPosition(...)` called getDefinitionAtPosition
158+
// LSP doesn't have two separate commands though. It's unclear how we would model bound spans though.
159+
return [parseBaselineGoToDefinitionArgs(callExpression.arguments)];
153160
}
154161
}
155162
// `goTo....`
@@ -668,6 +675,27 @@ function parseBaselineFindAllReferencesArgs(args: readonly ts.Expression[]): Ver
668675
};
669676
}
670677

678+
function parseBaselineGoToDefinitionArgs(args: readonly ts.Expression[]): VerifyBaselineGoToDefinitionCmd {
679+
const newArgs = [];
680+
for (const arg of args) {
681+
if (ts.isStringLiteral(arg)) {
682+
newArgs.push(getGoStringLiteral(arg.text));
683+
}
684+
else if (arg.getText() === "...test.ranges()") {
685+
return {
686+
kind: "verifyBaselineGoToDefinition",
687+
markers: [],
688+
ranges: true,
689+
};
690+
}
691+
}
692+
693+
return {
694+
kind: "verifyBaselineGoToDefinition",
695+
markers: newArgs,
696+
};
697+
}
698+
671699
function parseKind(expr: ts.Expression): string | undefined {
672700
if (!ts.isStringLiteral(expr)) {
673701
console.error(`Expected string literal for kind, got ${expr.getText()}`);
@@ -809,6 +837,12 @@ interface VerifyBaselineFindAllReferencesCmd {
809837
ranges?: boolean;
810838
}
811839

840+
interface VerifyBaselineGoToDefinitionCmd {
841+
kind: "verifyBaselineGoToDefinition";
842+
markers: string[];
843+
ranges?: boolean;
844+
}
845+
812846
interface GoToCmd {
813847
kind: "goTo";
814848
// !!! `selectRange` and `rangeStart` require parsing variables and `test.ranges()[n]`
@@ -821,7 +855,7 @@ interface EditCmd {
821855
goStatement: string;
822856
}
823857

824-
type Cmd = VerifyCompletionsCmd | VerifyBaselineFindAllReferencesCmd | GoToCmd | EditCmd;
858+
type Cmd = VerifyCompletionsCmd | VerifyBaselineFindAllReferencesCmd | VerifyBaselineGoToDefinitionCmd | GoToCmd | EditCmd;
825859

826860
function generateVerifyCompletions({ marker, args, isNewIdentifierLocation }: VerifyCompletionsCmd): string {
827861
let expectedList: string;
@@ -857,6 +891,13 @@ function generateBaselineFindAllReferences({ markers, ranges }: VerifyBaselineFi
857891
return `f.VerifyBaselineFindAllReferences(t, ${markers.join(", ")})`;
858892
}
859893

894+
function generateBaselineGoToDefinition({ markers, ranges }: VerifyBaselineGoToDefinitionCmd): string {
895+
if (ranges || markers.length === 0) {
896+
return `f.VerifyBaselineGoToDefinition(t)`;
897+
}
898+
return `f.VerifyBaselineGoToDefinition(t, ${markers.join(", ")})`;
899+
}
900+
860901
function generateGoToCommand({ funcName, args }: GoToCmd): string {
861902
const funcNameCapitalized = funcName.charAt(0).toUpperCase() + funcName.slice(1);
862903
return `f.GoTo${funcNameCapitalized}(t, ${args.join(", ")})`;
@@ -868,12 +909,15 @@ function generateCmd(cmd: Cmd): string {
868909
return generateVerifyCompletions(cmd as VerifyCompletionsCmd);
869910
case "verifyBaselineFindAllReferences":
870911
return generateBaselineFindAllReferences(cmd as VerifyBaselineFindAllReferencesCmd);
912+
case "verifyBaselineGoToDefinition":
913+
return generateBaselineGoToDefinition(cmd as VerifyBaselineGoToDefinitionCmd);
871914
case "goTo":
872915
return generateGoToCommand(cmd as GoToCmd);
873916
case "edit":
874917
return cmd.goStatement;
875918
default:
876-
throw new Error(`Unknown command kind: ${cmd}`);
919+
let neverCommand: never = cmd;
920+
throw new Error(`Unknown command kind: ${neverCommand as Cmd["kind"]}`);
877921
}
878922
}
879923

internal/fourslash/_scripts/failingTests.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ TestFindAllRefsCommonJsRequire2
184184
TestFindAllRefsCommonJsRequire3
185185
TestFindAllRefsExportEquals
186186
TestFindAllRefsForDefaultExport03
187-
TestFindAllRefsJsDocImportTag
188187
TestFindAllRefsModuleDotExports
189188
TestFindAllRefsReExport_broken
190189
TestFindAllRefs_importType_typeofImport
@@ -203,6 +202,9 @@ TestGetJavaScriptCompletions8
203202
TestGetJavaScriptCompletions9
204203
TestGetJavaScriptGlobalCompletions1
205204
TestGetJavaScriptQuickInfo8
205+
TestGoToDefinitionBuiltInTypes
206+
TestGoToDefinitionBuiltInValues
207+
TestGoToDefinitionUndefinedSymbols
206208
TestImportCompletionsPackageJsonExportsSpecifierEndsInTs
207209
TestImportCompletionsPackageJsonExportsTrailingSlash1
208210
TestImportCompletionsPackageJsonImportsConditions1

internal/fourslash/baselineutil.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ func (f *FourslashTest) getBaselineForLocationsWithFileContents(spans []*lsproto
5454
}
5555

5656
func (f *FourslashTest) getBaselineForGroupedLocationsWithFileContents(groupedLocations *collections.MultiMap[lsproto.DocumentUri, *lsproto.Location], options baselineFourslashLocationsOptions) string {
57+
// We must always print the file containing the marker,
58+
// but don't want to print it twice at the end if it already
59+
// found in a file with ranges.
60+
foundMarker := false
61+
5762
baselineEntries := []string{}
5863
err := f.vfs.WalkDir("/", func(path string, d vfs.DirEntry, e error) error {
5964
if e != nil {
@@ -64,16 +69,22 @@ func (f *FourslashTest) getBaselineForGroupedLocationsWithFileContents(groupedLo
6469
return nil
6570
}
6671

67-
locations := groupedLocations.Get(ls.FileNameToDocumentURI(path))
72+
fileName := ls.FileNameToDocumentURI(path)
73+
locations := groupedLocations.Get(fileName)
6874
if len(locations) == 0 {
6975
return nil
7076
}
7177

7278
content, ok := f.vfs.ReadFile(path)
7379
if !ok {
80+
// !!! error?
7481
return nil
7582
}
7683

84+
if options.marker != nil && options.marker.FileName() == path {
85+
foundMarker = true
86+
}
87+
7788
documentSpans := core.Map(locations, func(location *lsproto.Location) *documentSpan {
7889
return &documentSpan{
7990
Location: *location,
@@ -86,7 +97,15 @@ func (f *FourslashTest) getBaselineForGroupedLocationsWithFileContents(groupedLo
8697
if err != nil && !errors.Is(err, fs.ErrNotExist) {
8798
panic("walkdir error during fourslash baseline: " + err.Error())
8899
}
89-
// !!! foundMarker
100+
101+
if !foundMarker && options.marker != nil {
102+
// If we didn't find the marker in any file, we need to add it.
103+
markerFileName := options.marker.FileName()
104+
if content, ok := f.vfs.ReadFile(markerFileName); ok {
105+
baselineEntries = append(baselineEntries, f.getBaselineContentForFile(markerFileName, content, nil, nil, options))
106+
}
107+
}
108+
90109
// !!! foundAdditionalSpan
91110
// !!! skipDocumentContainingOnlyMarker
92111

internal/fourslash/fourslash.go

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -774,19 +774,7 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
774774
t *testing.T,
775775
markers ...string,
776776
) {
777-
// if there are no markers specified, use all ranges
778-
var referenceLocations []MarkerOrRange
779-
if len(markers) == 0 {
780-
referenceLocations = core.Map(f.testData.Ranges, func(r *RangeMarker) MarkerOrRange { return r })
781-
} else {
782-
referenceLocations = core.Map(markers, func(markerName string) MarkerOrRange {
783-
marker, ok := f.testData.MarkerPositions[markerName]
784-
if !ok {
785-
t.Fatalf("Marker '%s' not found", markerName)
786-
}
787-
return marker
788-
})
789-
}
777+
referenceLocations := f.lookupMarkersOrGetRanges(t, markers)
790778

791779
if f.baseline != nil {
792780
t.Fatalf("Error during test '%s': Another baseline is already in progress", t.Name())
@@ -806,21 +794,23 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
806794
for _, markerOrRange := range referenceLocations {
807795
// worker in `baselineEachMarkerOrRange`
808796
f.GoToMarkerOrRange(t, markerOrRange)
797+
809798
params := &lsproto.ReferenceParams{
810799
TextDocumentPositionParams: f.currentTextDocumentPositionParams(),
811800
Context: &lsproto.ReferenceContext{},
812801
}
813802
resMsg := f.sendRequest(t, lsproto.MethodTextDocumentReferences, params)
814803
if resMsg == nil {
815804
if f.lastKnownMarkerName == nil {
816-
t.Fatalf("Unexpected references response type at pos %v", f.currentCaretPosition)
805+
t.Fatalf("Nil response received for references request at pos %v", f.currentCaretPosition)
817806
} else {
818807
t.Fatalf("Nil response received for references request at marker '%s'", *f.lastKnownMarkerName)
819808
}
820809
}
810+
821811
result := resMsg.AsResponse().Result
822-
if result, ok := result.([]*lsproto.Location); ok {
823-
f.baseline.addResult("findAllReferences", f.getBaselineForLocationsWithFileContents(result, baselineFourslashLocationsOptions{
812+
if resultAsLocation, ok := result.([]*lsproto.Location); ok {
813+
f.baseline.addResult("findAllReferences", f.getBaselineForLocationsWithFileContents(resultAsLocation, baselineFourslashLocationsOptions{
824814
marker: markerOrRange.GetMarker(),
825815
markerName: "/*FIND ALL REFS*/",
826816
}))
@@ -832,9 +822,93 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
832822
}
833823
}
834824
}
825+
835826
baseline.Run(t, f.baseline.getBaselineFileName(), f.baseline.content.String(), baseline.Options{})
836827
}
837828

829+
func (f *FourslashTest) VerifyBaselineGoToDefinition(
830+
t *testing.T,
831+
markers ...string,
832+
) {
833+
referenceLocations := f.lookupMarkersOrGetRanges(t, markers)
834+
835+
if f.baseline != nil {
836+
t.Fatalf("Error during test '%s': Another baseline is already in progress", t.Name())
837+
} else {
838+
f.baseline = &baselineFromTest{
839+
content: &strings.Builder{},
840+
baselineName: "goToDef/" + strings.TrimPrefix(t.Name(), "Test"),
841+
ext: ".baseline.jsonc",
842+
}
843+
}
844+
845+
// empty baseline after test completes
846+
defer func() {
847+
f.baseline = nil
848+
}()
849+
850+
for _, markerOrRange := range referenceLocations {
851+
// worker in `baselineEachMarkerOrRange`
852+
f.GoToMarkerOrRange(t, markerOrRange)
853+
854+
params := &lsproto.DefinitionParams{
855+
TextDocumentPositionParams: f.currentTextDocumentPositionParams(),
856+
}
857+
resMsg := f.sendRequest(t, lsproto.MethodTextDocumentDefinition, params)
858+
if resMsg == nil {
859+
if f.lastKnownMarkerName == nil {
860+
t.Fatalf("Nil response received for definition request at pos %v", f.currentCaretPosition)
861+
} else {
862+
t.Fatalf("Nil response received for definition request at marker '%s'", *f.lastKnownMarkerName)
863+
}
864+
}
865+
866+
result := resMsg.AsResponse().Result
867+
if resultAsLocOrLocations, ok := result.(*lsproto.LocationOrLocations); ok {
868+
var resultAsLocations []*lsproto.Location
869+
if resultAsLocOrLocations != nil {
870+
if resultAsLocOrLocations.Locations != nil {
871+
resultAsLocations = core.Map(*resultAsLocOrLocations.Locations, func(loc lsproto.Location) *lsproto.Location {
872+
return &loc
873+
})
874+
} else {
875+
resultAsLocations = []*lsproto.Location{resultAsLocOrLocations.Location}
876+
}
877+
}
878+
879+
f.baseline.addResult("goToDefinition", f.getBaselineForLocationsWithFileContents(resultAsLocations, baselineFourslashLocationsOptions{
880+
marker: markerOrRange.GetMarker(),
881+
markerName: "/*GO TO DEFINITION*/",
882+
}))
883+
} else {
884+
if f.lastKnownMarkerName == nil {
885+
t.Fatalf("Unexpected definition response type at pos %v: %T", f.currentCaretPosition, result)
886+
} else {
887+
t.Fatalf("Unexpected definition response type at marker '%s': %T", *f.lastKnownMarkerName, result)
888+
}
889+
}
890+
}
891+
892+
baseline.Run(t, f.baseline.getBaselineFileName(), f.baseline.content.String(), baseline.Options{})
893+
}
894+
895+
// Collects all named markers if provided, or defaults to anonymous ranges
896+
func (f *FourslashTest) lookupMarkersOrGetRanges(t *testing.T, markers []string) []MarkerOrRange {
897+
var referenceLocations []MarkerOrRange
898+
if len(markers) == 0 {
899+
referenceLocations = core.Map(f.testData.Ranges, func(r *RangeMarker) MarkerOrRange { return r })
900+
} else {
901+
referenceLocations = core.Map(markers, func(markerName string) MarkerOrRange {
902+
marker, ok := f.testData.MarkerPositions[markerName]
903+
if !ok {
904+
t.Fatalf("Marker '%s' not found", markerName)
905+
}
906+
return marker
907+
})
908+
}
909+
return referenceLocations
910+
}
911+
838912
func ptrTo[T any](v T) *T {
839913
return &v
840914
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestDeclarationMapGoToDefinition(t *testing.T) {
11+
t.Parallel()
12+
13+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
14+
const content = `// @Filename: index.ts
15+
export class Foo {
16+
member: string;
17+
/*2*/methodName(propName: SomeType): void {}
18+
otherMethod() {
19+
if (Math.random() > 0.5) {
20+
return {x: 42};
21+
}
22+
return {y: "yes"};
23+
}
24+
}
25+
26+
export interface SomeType {
27+
member: number;
28+
}
29+
// @Filename: indexdef.d.ts.map
30+
{"version":3,"file":"indexdef.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;IACI,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"}
31+
// @Filename: indexdef.d.ts
32+
export declare class Foo {
33+
member: string;
34+
methodName(propName: SomeType): void;
35+
otherMethod(): {
36+
x: number;
37+
y?: undefined;
38+
} | {
39+
y: string;
40+
x?: undefined;
41+
};
42+
}
43+
export interface SomeType {
44+
member: number;
45+
}
46+
//# sourceMappingURL=indexdef.d.ts.map
47+
// @Filename: mymodule.ts
48+
import * as mod from "./indexdef";
49+
const instance = new mod.Foo();
50+
instance.[|/*1*/methodName|]({member: 12});`
51+
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
52+
f.VerifyBaselineGoToDefinition(t, "1")
53+
}

0 commit comments

Comments
 (0)