Skip to content

Commit 9275bc8

Browse files
gabrittoahejlsberg
andauthored
Port rename test + baseline fixes (#1714)
Co-authored-by: Anders Hejlsberg <[email protected]>
1 parent 4c9b8f6 commit 9275bc8

File tree

1,591 files changed

+36406
-25477
lines changed

Some content is hidden

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

1,591 files changed

+36406
-25477
lines changed

internal/fourslash/_scripts/convertFourslash.mts

Lines changed: 193 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function parseFileContent(filename: string, content: string): GoTest | undefined
9797
}
9898

9999
function getTestInput(content: string): string {
100-
const lines = content.split("\n");
100+
const lines = content.split("\n").map(line => line.endsWith("\r") ? line.slice(0, -1) : line);
101101
let testInput: string[] = [];
102102
for (const line of lines) {
103103
let newLine = "";
@@ -118,7 +118,14 @@ function getTestInput(content: string): string {
118118
}
119119

120120
// chomp leading spaces
121-
if (!testInput.some(line => line.length != 0 && !line.startsWith(" ") && !line.startsWith("// "))) {
121+
if (
122+
!testInput.some(line =>
123+
line.length != 0 &&
124+
!line.startsWith(" ") &&
125+
!line.startsWith("// ") &&
126+
!line.startsWith("//@")
127+
)
128+
) {
122129
testInput = testInput.map(line => {
123130
if (line.startsWith(" ")) return line.substring(1);
124131
return line;
@@ -182,6 +189,13 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
182189
// - `verify.baselineGetDefinitionAtPosition(...)` called getDefinitionAtPosition
183190
// LSP doesn't have two separate commands though. It's unclear how we would model bound spans though.
184191
return parseBaselineGoToDefinitionArgs(callExpression.arguments);
192+
case "baselineRename":
193+
case "baselineRenameAtRangesWithText":
194+
// `verify.baselineRename...(...)`
195+
return parseBaselineRenameArgs(func.text, callExpression.arguments);
196+
case "renameInfoSucceeded":
197+
case "renameInfoFailed":
198+
return parseRenameInfo(func.text, callExpression.arguments);
185199
}
186200
}
187201
// `goTo....`
@@ -793,6 +807,151 @@ function parseBaselineGoToDefinitionArgs(args: readonly ts.Expression[]): [Verif
793807
}];
794808
}
795809

810+
function parseRenameInfo(funcName: "renameInfoSucceeded" | "renameInfoFailed", args: readonly ts.Expression[]): [VerifyRenameInfoCmd] | undefined {
811+
let preferences = "nil /*preferences*/";
812+
let prefArg;
813+
switch (funcName) {
814+
case "renameInfoSucceeded":
815+
if (args[6]) {
816+
prefArg = args[6];
817+
}
818+
break;
819+
case "renameInfoFailed":
820+
if (args[1]) {
821+
prefArg = args[1];
822+
}
823+
break;
824+
}
825+
if (prefArg) {
826+
if (!ts.isObjectLiteralExpression(prefArg)) {
827+
console.error(`Expected object literal expression for preferences, got ${prefArg.getText()}`);
828+
return undefined;
829+
}
830+
const parsedPreferences = parseUserPreferences(prefArg);
831+
if (!parsedPreferences) {
832+
console.error(`Unrecognized user preferences in ${funcName}: ${prefArg.getText()}`);
833+
return undefined;
834+
}
835+
}
836+
return [{ kind: funcName, preferences }];
837+
}
838+
839+
function parseBaselineRenameArgs(funcName: string, args: readonly ts.Expression[]): [VerifyBaselineRenameCmd] | undefined {
840+
let newArgs: string[] = [];
841+
let preferences: string | undefined;
842+
for (const arg of args) {
843+
let typedArg;
844+
if ((typedArg = getArrayLiteralExpression(arg))) {
845+
for (const elem of typedArg.elements) {
846+
const newArg = parseBaselineRenameArg(elem);
847+
if (!newArg) {
848+
return undefined;
849+
}
850+
newArgs.push(newArg);
851+
}
852+
}
853+
else if (ts.isObjectLiteralExpression(arg)) {
854+
preferences = parseUserPreferences(arg);
855+
if (!preferences) {
856+
console.error(`Unrecognized user preferences in verify.baselineRename: ${arg.getText()}`);
857+
return undefined;
858+
}
859+
continue;
860+
}
861+
else if (typedArg = parseBaselineRenameArg(arg)) {
862+
newArgs.push(typedArg);
863+
}
864+
else {
865+
return undefined;
866+
}
867+
}
868+
return [{
869+
kind: funcName === "baselineRenameAtRangesWithText" ? "verifyBaselineRenameAtRangesWithText" : "verifyBaselineRename",
870+
args: newArgs,
871+
preferences: preferences ? preferences : "nil /*preferences*/",
872+
}];
873+
}
874+
875+
function parseUserPreferences(arg: ts.ObjectLiteralExpression): string | undefined {
876+
const preferences: string[] = [];
877+
for (const prop of arg.properties) {
878+
if (ts.isPropertyAssignment(prop)) {
879+
switch (prop.name.getText()) {
880+
// !!! other preferences
881+
case "providePrefixAndSuffixTextForRename":
882+
preferences.push(`UseAliasesForRename: PtrTo(${prop.initializer.getText()})`);
883+
break;
884+
case "quotePreference":
885+
preferences.push(`QuotePreference: PtrTo(ls.QuotePreference(${prop.initializer.getText()}))`);
886+
break;
887+
}
888+
}
889+
else {
890+
return undefined;
891+
}
892+
}
893+
if (preferences.length === 0) {
894+
return "nil /*preferences*/";
895+
}
896+
return `&ls.UserPreferences{${preferences.join(",")}}`;
897+
}
898+
899+
function parseBaselineRenameArg(arg: ts.Expression): string | undefined {
900+
if (ts.isStringLiteral(arg)) {
901+
return getGoStringLiteral(arg.text);
902+
}
903+
else if (ts.isIdentifier(arg) || (ts.isElementAccessExpression(arg) && ts.isIdentifier(arg.expression))) {
904+
const argName = ts.isIdentifier(arg) ? arg.text : (arg.expression as ts.Identifier).text;
905+
const file = arg.getSourceFile();
906+
const varStmts = file.statements.filter(ts.isVariableStatement);
907+
for (const varStmt of varStmts) {
908+
for (const decl of varStmt.declarationList.declarations) {
909+
if (ts.isArrayBindingPattern(decl.name) && decl.initializer?.getText().includes("ranges")) {
910+
for (let i = 0; i < decl.name.elements.length; i++) {
911+
const elem = decl.name.elements[i];
912+
if (ts.isBindingElement(elem) && ts.isIdentifier(elem.name) && elem.name.text === argName) {
913+
// `const [range_0, ..., range_n, ...] = test.ranges();` and arg is `range_n`
914+
if (elem.dotDotDotToken === undefined) {
915+
return `f.Ranges()[${i}]`;
916+
}
917+
// `const [range_0, ..., ...rest] = test.ranges();` and arg is `rest[n]`
918+
if (ts.isElementAccessExpression(arg)) {
919+
return `f.Ranges()[${i + parseInt(arg.argumentExpression!.getText())}]`;
920+
}
921+
// `const [range_0, ..., ...rest] = test.ranges();` and arg is `rest`
922+
return `ToAny(f.Ranges()[${i}:])...`;
923+
}
924+
}
925+
}
926+
}
927+
}
928+
const init = getNodeOfKind(arg, ts.isCallExpression);
929+
if (init) {
930+
const result = getRangesByTextArg(init);
931+
if (result) {
932+
return result;
933+
}
934+
}
935+
}
936+
else if (ts.isCallExpression(arg)) {
937+
const result = getRangesByTextArg(arg);
938+
if (result) {
939+
return result;
940+
}
941+
}
942+
console.error(`Unrecognized argument in verify.baselineRename: ${arg.getText()}`);
943+
return undefined;
944+
}
945+
946+
function getRangesByTextArg(arg: ts.CallExpression): string | undefined {
947+
if (arg.getText().startsWith("test.rangesByText()")) {
948+
if (ts.isStringLiteralLike(arg.arguments[0])) {
949+
return `ToAny(f.GetRangesByText().Get(${getGoStringLiteral(arg.arguments[0].text)}))...`;
950+
}
951+
}
952+
return undefined;
953+
}
954+
796955
function parseBaselineQuickInfo(args: ts.NodeArray<ts.Expression>): VerifyBaselineQuickInfoCmd {
797956
if (args.length !== 0) {
798957
// All calls are currently empty!
@@ -1097,6 +1256,12 @@ interface VerifyBaselineSignatureHelpCmd {
10971256
kind: "verifyBaselineSignatureHelp";
10981257
}
10991258

1259+
interface VerifyBaselineRenameCmd {
1260+
kind: "verifyBaselineRename" | "verifyBaselineRenameAtRangesWithText";
1261+
args: string[];
1262+
preferences: string;
1263+
}
1264+
11001265
interface GoToCmd {
11011266
kind: "goTo";
11021267
// !!! `selectRange` and `rangeStart` require parsing variables and `test.ranges()[n]`
@@ -1116,6 +1281,11 @@ interface VerifyQuickInfoCmd {
11161281
docs?: string;
11171282
}
11181283

1284+
interface VerifyRenameInfoCmd {
1285+
kind: "renameInfoSucceeded" | "renameInfoFailed";
1286+
preferences: string;
1287+
}
1288+
11191289
type Cmd =
11201290
| VerifyCompletionsCmd
11211291
| VerifyBaselineFindAllReferencesCmd
@@ -1124,7 +1294,9 @@ type Cmd =
11241294
| VerifyBaselineSignatureHelpCmd
11251295
| GoToCmd
11261296
| EditCmd
1127-
| VerifyQuickInfoCmd;
1297+
| VerifyQuickInfoCmd
1298+
| VerifyBaselineRenameCmd
1299+
| VerifyRenameInfoCmd;
11281300

11291301
function generateVerifyCompletions({ marker, args, isNewIdentifierLocation }: VerifyCompletionsCmd): string {
11301302
let expectedList: string;
@@ -1185,6 +1357,15 @@ function generateQuickInfoCommand({ kind, marker, text, docs }: VerifyQuickInfoC
11851357
}
11861358
}
11871359

1360+
function generateBaselineRename({ kind, args, preferences }: VerifyBaselineRenameCmd): string {
1361+
switch (kind) {
1362+
case "verifyBaselineRename":
1363+
return `f.VerifyBaselineRename(t, ${preferences}, ${args.join(", ")})`;
1364+
case "verifyBaselineRenameAtRangesWithText":
1365+
return `f.VerifyBaselineRenameAtRangesWithText(t, ${preferences}, ${args.join(", ")})`;
1366+
}
1367+
}
1368+
11881369
function generateCmd(cmd: Cmd): string {
11891370
switch (cmd.kind) {
11901371
case "verifyCompletions":
@@ -1207,6 +1388,13 @@ function generateCmd(cmd: Cmd): string {
12071388
case "quickInfoExists":
12081389
case "notQuickInfoExists":
12091390
return generateQuickInfoCommand(cmd);
1391+
case "verifyBaselineRename":
1392+
case "verifyBaselineRenameAtRangesWithText":
1393+
return generateBaselineRename(cmd);
1394+
case "renameInfoSucceeded":
1395+
return `f.VerifyRenameSucceeded(t, ${cmd.preferences})`;
1396+
case "renameInfoFailed":
1397+
return `f.VerifyRenameFailed(t, ${cmd.preferences})`;
12101398
default:
12111399
let neverCommand: never = cmd;
12121400
throw new Error(`Unknown command kind: ${neverCommand as Cmd["kind"]}`);
@@ -1267,7 +1455,8 @@ function usesHelper(goTxt: string): boolean {
12671455
}
12681456
return goTxt.includes("Ignored")
12691457
|| goTxt.includes("DefaultCommitCharacters")
1270-
|| goTxt.includes("PtrTo");
1458+
|| goTxt.includes("PtrTo")
1459+
|| goTxt.includes("ToAny");
12711460
}
12721461

12731462
function getNodeOfKind<T extends ts.Node>(node: ts.Node, hasKind: (n: ts.Node) => n is T): T | undefined {

internal/fourslash/_scripts/failingTests.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ TestJsDocPropertyDescription6
249249
TestJsDocPropertyDescription7
250250
TestJsDocPropertyDescription8
251251
TestJsDocPropertyDescription9
252+
TestJsDocSee_rename1
252253
TestJsDocTagsWithHyphen
253254
TestJsQuickInfoGenerallyAcceptableSize
254255
TestJsRequireQuickInfo
@@ -257,6 +258,7 @@ TestJsdocLink2
257258
TestJsdocLink3
258259
TestJsdocLink6
259260
TestJsdocLink_findAllReferences1
261+
TestJsdocLink_rename1
260262
TestJsdocTemplatePrototypeCompletions
261263
TestJsdocThrowsTagCompletion
262264
TestJsdocTypedefTag
@@ -438,6 +440,13 @@ TestReferencesInComment
438440
TestReferencesInEmptyFile
439441
TestReferencesIsAvailableThroughGlobalNoCrash
440442
TestRegexDetection
443+
TestRenameCrossJsTs01
444+
TestRenameForAliasingExport02
445+
TestRenameFromNodeModulesDep1
446+
TestRenameFromNodeModulesDep2
447+
TestRenameFromNodeModulesDep3
448+
TestRenameFromNodeModulesDep4
449+
TestRenamePrivateFields
441450
TestReverseMappedTypeQuickInfo
442451
TestSelfReferencedExternalModule
443452
TestSignatureHelpInferenceJsDocImportTag

internal/fourslash/_scripts/makeManual.mts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const scriptsDir = import.meta.dirname;
55
const manualTestsPath = path.join(scriptsDir, "manualTests.txt");
66
const genDir = path.join(scriptsDir, "../", "tests", "gen");
77
const manualDir = path.join(scriptsDir, "../", "tests", "manual");
8+
const submoduleDir = path.join(scriptsDir, "../../../", "_submodules", "TypeScript", "tests", "cases", "fourslash");
89

910
function main() {
1011
const args = process.argv.slice(2);
@@ -17,17 +18,35 @@ function main() {
1718
const testName = args[0];
1819
const testFileName = testName;
1920
const genTestFile = path.join(genDir, testFileName + "_test.go");
20-
if (!fs.existsSync(genTestFile)) {
21-
console.error(`Test file not found: '${genTestFile}'. Make sure the test exists in the gen directory first.`);
21+
const submoduleTestFile = path.join(submoduleDir, testFileName + ".ts");
22+
const submoduleServerTestFile = path.join(submoduleDir, "server", testFileName + ".ts");
23+
let testKind: "gen" | "submodule" | "submoduleServer" | undefined;
24+
if (fs.existsSync(genTestFile)) {
25+
testKind = "gen";
26+
}
27+
else if (fs.existsSync(submoduleTestFile)) {
28+
testKind = "submodule";
29+
}
30+
else if (fs.existsSync(submoduleServerTestFile)) {
31+
testKind = "submoduleServer";
32+
}
33+
34+
if (!testKind) {
35+
console.error(
36+
`Could not find test neither as '${genTestFile}', nor as '${submoduleTestFile}' or '${submoduleServerTestFile}'.` +
37+
`Make sure the test exists in the gen directory or in the submodule.`,
38+
);
2239
process.exit(1);
2340
}
2441

2542
if (!fs.existsSync(manualDir)) {
2643
fs.mkdirSync(manualDir, { recursive: true });
2744
}
2845

29-
const manualTestFile = path.join(manualDir, path.basename(genTestFile));
30-
renameAndRemoveSkip(genTestFile, manualTestFile);
46+
if (testKind === "gen") {
47+
const manualTestFile = path.join(manualDir, path.basename(genTestFile));
48+
markAsManual(genTestFile, manualTestFile);
49+
}
3150

3251
let manualTests: string[] = [];
3352
if (fs.existsSync(manualTestsPath)) {
@@ -42,7 +61,7 @@ function main() {
4261
}
4362
}
4463

45-
function renameAndRemoveSkip(genFilePath: string, manualFilePath: string) {
64+
function markAsManual(genFilePath: string, manualFilePath: string) {
4665
const content = fs.readFileSync(genFilePath, "utf-8");
4766
const updatedContent = content.replace(/^\s*t\.Skip\(\)\s*$/m, "");
4867
fs.writeFileSync(manualFilePath, updatedContent, "utf-8");

internal/fourslash/_scripts/manualTests.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ completionListInClosedFunction05
22
completionsAtIncompleteObjectLiteralProperty
33
completionsSelfDeclaring1
44
completionsWithDeprecatedTag4
5+
renameDefaultKeyword
6+
renameForDefaultExport01
57
tsxCompletion12

internal/fourslash/_scripts/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"strict": true,
44
"noEmit": true,
55
"module": "nodenext",
6-
"allowImportingTsExtensions": true
6+
"allowImportingTsExtensions": true,
7+
"noFallthroughCasesInSwitch": true
78
}
89
}

0 commit comments

Comments
 (0)