Skip to content

Commit 0c78eff

Browse files
committed
Address false positive cases for return value codefix
1 parent e4950b2 commit 0c78eff

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

src/compiler/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3568,9 +3568,9 @@ namespace ts {
35683568
/* @internal */ createAnonymousType(symbol: Symbol | undefined, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): Type;
35693569
/* @internal */ createSignature(
35703570
declaration: SignatureDeclaration,
3571-
typeParameters: TypeParameter[] | undefined,
3571+
typeParameters: readonly TypeParameter[] | undefined,
35723572
thisParameter: Symbol | undefined,
3573-
parameters: Symbol[],
3573+
parameters: readonly Symbol[],
35743574
resolvedReturnType: Type,
35753575
typePredicate: TypePredicate | undefined,
35763576
minArgumentCount: number,

src/services/codefixes/returnValueCorrect.ts

+41-5
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,18 @@ namespace ts.codefix {
7272
}),
7373
});
7474

75+
function createObjectTypeFromLabeledExpression(checker: TypeChecker, label: Identifier, expression: Expression) {
76+
const member = checker.createSymbol(SymbolFlags.Property, label.escapedText);
77+
member.type = checker.getTypeAtLocation(expression);
78+
const members = createSymbolTable([member]);
79+
return checker.createAnonymousType(/*symbol*/ undefined, members, [], [], /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
80+
}
81+
7582
function getFixInfo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type, isFunctionType: boolean): Info | undefined {
7683
if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined;
7784

7885
const firstStatement = first(declaration.body.statements);
79-
if (isExpressionStatement(firstStatement) && checkFixedAssignableTo(checker, declaration, firstStatement.expression, expectType, isFunctionType)) {
86+
if (isExpressionStatement(firstStatement) && checkFixedAssignableTo(checker, declaration, checker.getTypeAtLocation(firstStatement.expression), expectType, isFunctionType)) {
8087
return {
8188
declaration,
8289
kind: ProblemKind.MissingReturnStatement,
@@ -87,7 +94,8 @@ namespace ts.codefix {
8794
}
8895
else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) {
8996
const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]);
90-
if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) {
97+
const nodeType = createObjectTypeFromLabeledExpression(checker, firstStatement.label, firstStatement.statement.expression);
98+
if (checkFixedAssignableTo(checker, declaration, nodeType, expectType, isFunctionType)) {
9199
return isArrowFunction(declaration) ? {
92100
declaration,
93101
kind: ProblemKind.MissingParentheses,
@@ -107,7 +115,8 @@ namespace ts.codefix {
107115
const firstBlockStatement = first(firstStatement.statements);
108116
if (isLabeledStatement(firstBlockStatement) && isExpressionStatement(firstBlockStatement.statement)) {
109117
const node = createObjectLiteral([createPropertyAssignment(firstBlockStatement.label, firstBlockStatement.statement.expression)]);
110-
if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) {
118+
const nodeType = createObjectTypeFromLabeledExpression(checker, firstBlockStatement.label, firstBlockStatement.statement.expression);
119+
if (checkFixedAssignableTo(checker, declaration, nodeType, expectType, isFunctionType)) {
111120
return {
112121
declaration,
113122
kind: ProblemKind.MissingReturnStatement,
@@ -122,8 +131,35 @@ namespace ts.codefix {
122131
return undefined;
123132
}
124133

125-
function checkFixedAssignableTo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expr: Expression, type: Type, isFunctionType: boolean) {
126-
return checker.isTypeAssignableTo(checker.getTypeAtLocation(isFunctionType ? updateFunctionLikeBody(declaration, createBlock([createReturn(expr)])) : expr), type);
134+
function checkFixedAssignableTo(checker: TypeChecker, declaration: FunctionLikeDeclaration, exprType: Type, type: Type, isFunctionType: boolean) {
135+
if (isFunctionType) {
136+
const sig = checker.getSignatureFromDeclaration(declaration);
137+
if (sig) {
138+
if (hasModifier(declaration, ModifierFlags.Async)) {
139+
exprType = checker.createPromiseType(exprType);
140+
}
141+
const newSig = checker.createSignature(
142+
declaration,
143+
sig.typeParameters,
144+
sig.thisParameter,
145+
sig.parameters,
146+
exprType,
147+
/*typePredicate*/ undefined,
148+
sig.minArgumentCount,
149+
sig.flags);
150+
exprType = checker.createAnonymousType(
151+
/*symbol*/ undefined,
152+
createSymbolTable(),
153+
[newSig],
154+
[],
155+
/*stringIndexInfo*/ undefined,
156+
/*numberIndexInfo*/ undefined);
157+
}
158+
else {
159+
exprType = checker.getAnyType();
160+
}
161+
}
162+
return checker.isTypeAssignableTo(exprType, type);
127163
}
128164

129165
function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number, errorCode: number): Info | undefined {

tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//// await Promise.resolve('foo');
55
////}
66

7+
debugger;
78
verify.codeFix({
89
index: 2,
910
description: "Add async modifier to containing function",

0 commit comments

Comments
 (0)