Skip to content

Commit 95c1e6b

Browse files
Andaristgabritto
andcommitted
Port return conditional expression checking improvements
Co-authored-by: Gabriela Araujo Britto <[email protected]>
1 parent ddb1ebb commit 95c1e6b

File tree

4 files changed

+46
-66
lines changed

4 files changed

+46
-66
lines changed

internal/ast/ast.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5984,6 +5984,10 @@ func (node *ConditionalExpression) computeSubtreeFacts() SubtreeFacts {
59845984
propagateSubtreeFacts(node.WhenFalse)
59855985
}
59865986

5987+
func IsConditionalExpression(node *Node) bool {
5988+
return node.Kind == KindConditionalExpression
5989+
}
5990+
59875991
// PropertyAccessExpression
59885992

59895993
type PropertyAccessExpression struct {

internal/checker/checker.go

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3883,23 +3883,41 @@ func (c *Checker) checkReturnStatement(node *ast.Node) {
38833883
}
38843884
} else if c.getReturnTypeFromAnnotation(container) != nil {
38853885
unwrappedReturnType := core.OrElse(c.unwrapReturnType(returnType, functionFlags), returnType)
3886-
unwrappedExprType := exprType
3887-
if functionFlags&FunctionFlagsAsync != 0 {
3888-
unwrappedExprType = c.checkAwaitedType(exprType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
3889-
}
3890-
if unwrappedReturnType != nil {
3891-
// If the function has a return type, but promisedType is
3892-
// undefined, an error will be reported in checkAsyncFunctionReturnType
3893-
// so we don't need to report one here.
3894-
c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, node, exprNode, nil, nil)
3895-
}
3886+
c.checkReturnExpression(container, unwrappedReturnType, node, node.Expression(), exprType, false)
38963887
}
38973888
} else if !ast.IsConstructorDeclaration(container) && c.compilerOptions.NoImplicitReturns.IsTrue() && !c.isUnwrappedReturnTypeUndefinedVoidOrAny(container, returnType) {
38983889
// The function has a return type, but the return statement doesn't have an expression.
38993890
c.error(node, diagnostics.Not_all_code_paths_return_a_value)
39003891
}
39013892
}
39023893

3894+
// When checking an arrow expression such as `(x) => exp`, then `node` is the expression `exp`.
3895+
// Otherwise, `node` is a return statement.
3896+
func (c *Checker) checkReturnExpression(container *ast.Node, unwrappedReturnType *Type, node *ast.Node, expr *ast.Node, exprType *Type, inConditionalExpression bool) {
3897+
unwrappedExprType := exprType
3898+
functionFlags := getFunctionFlags(container)
3899+
if expr != nil {
3900+
unwrappedExpr := ast.SkipParentheses(expr)
3901+
if ast.IsConditionalExpression(unwrappedExpr) {
3902+
whenTrue := unwrappedExpr.AsConditionalExpression().WhenTrue
3903+
whenFalse := unwrappedExpr.AsConditionalExpression().WhenFalse
3904+
c.checkReturnExpression(container, unwrappedReturnType, node, whenTrue, c.checkExpression(whenTrue), true /*inConditionalExpression*/)
3905+
c.checkReturnExpression(container, unwrappedReturnType, node, whenFalse, c.checkExpression(whenFalse), true /*inConditionalExpression*/)
3906+
return
3907+
}
3908+
}
3909+
inReturnStatement := node.Kind == ast.KindReturnStatement
3910+
if functionFlags&FunctionFlagsAsync != 0 {
3911+
unwrappedExprType = c.checkAwaitedType(exprType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
3912+
}
3913+
effectiveExpr := expr // The effective expression for diagnostics purposes.
3914+
if expr != nil {
3915+
effectiveExpr = c.getEffectiveCheckNode(expr)
3916+
}
3917+
errorNode := core.IfElse(inReturnStatement && !inConditionalExpression, node, effectiveExpr)
3918+
c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil)
3919+
}
3920+
39033921
func (c *Checker) checkWithStatement(node *ast.Node) {
39043922
if !c.checkGrammarStatementInAmbientContext(node) {
39053923
if node.Flags&ast.NodeFlagsAwaitContext != 0 {
@@ -9775,13 +9793,7 @@ func (c *Checker) checkFunctionExpressionOrObjectLiteralMethodDeferred(node *ast
97759793
if returnType != nil {
97769794
returnOrPromisedType := c.unwrapReturnType(returnType, functionFlags)
97779795
if returnOrPromisedType != nil {
9778-
effectiveCheckNode := c.getEffectiveCheckNode(body)
9779-
if (functionFlags & FunctionFlagsAsyncGenerator) == FunctionFlagsAsync {
9780-
awaitedType := c.checkAwaitedType(exprType, false /*withAlias*/, effectiveCheckNode, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
9781-
c.checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode, nil, nil)
9782-
} else {
9783-
c.checkTypeAssignableToAndOptionallyElaborate(exprType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode, nil, nil)
9784-
}
9796+
c.checkReturnExpression(node, returnOrPromisedType, body, body, exprType, false)
97859797
}
97869798
}
97879799
}
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
conditionalReturnExpression.ts(2,5): error TS2322: Type '1 | 2' is not assignable to type '3'.
2-
Type '1' is not assignable to type '3'.
1+
conditionalReturnExpression.ts(2,18): error TS2322: Type '1' is not assignable to type '3'.
2+
conditionalReturnExpression.ts(2,23): error TS2322: Type '2' is not assignable to type '3'.
3+
conditionalReturnExpression.ts(8,43): error TS2322: Type 'number' is not assignable to type 'string'.
4+
conditionalReturnExpression.ts(19,71): error TS2322: Type 'number' is not assignable to type 'string'.
35

46

5-
==== conditionalReturnExpression.ts (1 errors) ====
7+
==== conditionalReturnExpression.ts (4 errors) ====
68
function return1(x: boolean): 3 {
79
return (x ? (1) : 2);
8-
~~~~~~
9-
!!! error TS2322: Type '1 | 2' is not assignable to type '3'.
10-
!!! error TS2322: Type '1' is not assignable to type '3'.
10+
~
11+
!!! error TS2322: Type '1' is not assignable to type '3'.
12+
~
13+
!!! error TS2322: Type '2' is not assignable to type '3'.
1114
}
1215

1316
declare function getAny(): any;
1417

1518
function return2(x: string): string {
1619
return x.startsWith("a") ? getAny() : 1;
20+
~
21+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
1722
}
1823

1924
function return3(x: string): string {
@@ -25,5 +30,7 @@ conditionalReturnExpression.ts(2,5): error TS2322: Type '1 | 2' is not assignabl
2530
}
2631

2732
const return5 = (x: string): string => x.startsWith("a") ? getAny() : 1;
33+
~
34+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
2835

2936
const return6 = (x: string): string => (x.startsWith("a") ? getAny() : 1) as string;

testdata/baselines/reference/submodule/compiler/conditionalReturnExpression.errors.txt.diff

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)