Skip to content

Commit 5d81b17

Browse files
authored
fix(40042): add modifiers in correct position for decorated methods (microsoft#40050)
1 parent e4c1568 commit 5d81b17

7 files changed

+164
-14
lines changed

src/services/codefixes/convertToAsyncFunction.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ namespace ts.codefix {
6767
const functionToConvertRenamed = renameCollidingVarNames(functionToConvert, checker, synthNamesMap, context.sourceFile);
6868
const returnStatements = functionToConvertRenamed.body && isBlock(functionToConvertRenamed.body) ? getReturnStatementsWithPromiseHandlers(functionToConvertRenamed.body) : emptyArray;
6969
const transformer: Transformer = { checker, synthNamesMap, setOfExpressionsToReturn, isInJSFile: isInJavascript };
70-
7170
if (!returnStatements.length) {
7271
return;
7372
}
7473

75-
// add the async keyword
76-
changes.insertLastModifierBefore(sourceFile, SyntaxKind.AsyncKeyword, functionToConvert);
74+
const pos = functionToConvert.modifiers ? functionToConvert.modifiers.end :
75+
functionToConvert.decorators ? skipTrivia(sourceFile.text, functionToConvert.decorators.end) :
76+
functionToConvert.getStart(sourceFile);
77+
const options = functionToConvert.modifiers ? { prefix: " " } : { suffix: " " };
78+
changes.insertModifierAt(sourceFile, pos, SyntaxKind.AsyncKeyword, options);
7779

7880
for (const returnStatement of returnStatements) {
7981
forEachChild(returnStatement, function visit(node) {

src/services/textChanges.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -399,19 +399,12 @@ namespace ts.textChanges {
399399
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, options), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween));
400400
}
401401

402-
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
403-
const pos = before.getStart(sourceFile);
404-
this.insertNodeAt(sourceFile, pos, factory.createToken(modifier), { suffix: " " });
402+
public insertModifierAt(sourceFile: SourceFile, pos: number, modifier: SyntaxKind, options: InsertNodeOptions = {}): void {
403+
this.insertNodeAt(sourceFile, pos, factory.createToken(modifier), options);
405404
}
406405

407-
public insertLastModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
408-
if (!before.modifiers) {
409-
this.insertModifierBefore(sourceFile, modifier, before);
410-
return;
411-
}
412-
413-
const pos = before.modifiers.end;
414-
this.insertNodeAt(sourceFile, pos, factory.createToken(modifier), { prefix: " " });
406+
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
407+
return this.insertModifierAt(sourceFile, before.getStart(sourceFile), modifier, { suffix: " " });
415408
}
416409

417410
public insertCommentBeforeLine(sourceFile: SourceFile, lineNumber: number, position: number, commentText: string): void {

src/testRunner/unittests/services/convertToAsyncFunction.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,57 @@ function [#|foo|]() {
14331433
return fetch('b').then(() => 'c');
14341434
})
14351435
}
1436+
`);
1437+
_testConvertToAsyncFunction("convertToAsyncFunction_decoratedMethod", `
1438+
function decorator() {
1439+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
1440+
}
1441+
class Foo {
1442+
@decorator()
1443+
[#|method|]() {
1444+
return fetch('a').then(x => x);
1445+
}
1446+
}
1447+
`);
1448+
1449+
_testConvertToAsyncFunction("convertToAsyncFunction_decoratedMethodWithSingleLineComment", `
1450+
function decorator() {
1451+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
1452+
}
1453+
class Foo {
1454+
@decorator()
1455+
// comment
1456+
[#|method|]() {
1457+
return fetch('a').then(x => x);
1458+
}
1459+
}
1460+
`);
1461+
1462+
_testConvertToAsyncFunction("convertToAsyncFunction_decoratedMethodWithMultipleLineComment", `
1463+
function decorator() {
1464+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
1465+
}
1466+
class Foo {
1467+
@decorator()
1468+
/**
1469+
* comment
1470+
*/
1471+
[#|method|]() {
1472+
return fetch('a').then(x => x);
1473+
}
1474+
}
1475+
`);
1476+
1477+
_testConvertToAsyncFunction("convertToAsyncFunction_decoratedMethodWithModifier", `
1478+
function decorator() {
1479+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
1480+
}
1481+
class Foo {
1482+
@decorator()
1483+
public [#|method|]() {
1484+
return fetch('a').then(x => x);
1485+
}
1486+
}
14361487
`);
14371488

14381489
_testConvertToAsyncFunctionFailedSuggestion("convertToAsyncFunction_OutermostOnlyFailure", `
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ==ORIGINAL==
2+
3+
function decorator() {
4+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
5+
}
6+
class Foo {
7+
@decorator()
8+
/*[#|*/method/*|]*/() {
9+
return fetch('a').then(x => x);
10+
}
11+
}
12+
13+
// ==ASYNC FUNCTION::Convert to async function==
14+
15+
function decorator() {
16+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
17+
}
18+
class Foo {
19+
@decorator()
20+
async method() {
21+
const x = await fetch('a');
22+
return x;
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ==ORIGINAL==
2+
3+
function decorator() {
4+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
5+
}
6+
class Foo {
7+
@decorator()
8+
public /*[#|*/method/*|]*/() {
9+
return fetch('a').then(x => x);
10+
}
11+
}
12+
13+
// ==ASYNC FUNCTION::Convert to async function==
14+
15+
function decorator() {
16+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
17+
}
18+
class Foo {
19+
@decorator()
20+
public async method() {
21+
const x = await fetch('a');
22+
return x;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// ==ORIGINAL==
2+
3+
function decorator() {
4+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
5+
}
6+
class Foo {
7+
@decorator()
8+
/**
9+
* comment
10+
*/
11+
/*[#|*/method/*|]*/() {
12+
return fetch('a').then(x => x);
13+
}
14+
}
15+
16+
// ==ASYNC FUNCTION::Convert to async function==
17+
18+
function decorator() {
19+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
20+
}
21+
class Foo {
22+
@decorator()
23+
/**
24+
* comment
25+
*/
26+
async method() {
27+
const x = await fetch('a');
28+
return x;
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ==ORIGINAL==
2+
3+
function decorator() {
4+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
5+
}
6+
class Foo {
7+
@decorator()
8+
// comment
9+
/*[#|*/method/*|]*/() {
10+
return fetch('a').then(x => x);
11+
}
12+
}
13+
14+
// ==ASYNC FUNCTION::Convert to async function==
15+
16+
function decorator() {
17+
return (target: any, key: any, descriptor: PropertyDescriptor) => descriptor;
18+
}
19+
class Foo {
20+
@decorator()
21+
// comment
22+
async method() {
23+
const x = await fetch('a');
24+
return x;
25+
}
26+
}

0 commit comments

Comments
 (0)