Skip to content

Commit 06a7365

Browse files
authored
Don’t error for missing await when promise is referenced in condition body (#43593)
1 parent d601221 commit 06a7365

12 files changed

+326
-83
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35371,15 +35371,6 @@ namespace ts {
3537135371
if (!strictNullChecks) return;
3537235372
if (getFalsyFlags(type)) return;
3537335373

35374-
if (getAwaitedTypeOfPromise(type)) {
35375-
errorAndMaybeSuggestAwait(
35376-
condExpr,
35377-
/*maybeMissingAwait*/ true,
35378-
Diagnostics.This_condition_will_always_return_true_since_this_0_appears_to_always_be_defined,
35379-
getTypeNameForErrorDisplay(type));
35380-
return;
35381-
}
35382-
3538335374
const location = isBinaryExpression(condExpr) ? condExpr.right : condExpr;
3538435375
const testedNode = isIdentifier(location) ? location
3538535376
: isPropertyAccessExpression(location) ? location.name
@@ -35392,12 +35383,13 @@ namespace ts {
3539235383
}
3539335384

3539435385
// While it technically should be invalid for any known-truthy value
35395-
// to be tested, we de-scope to functions unrefenced in the block as a
35396-
// heuristic to identify the most common bugs. There are too many
35397-
// false positives for values sourced from type definitions without
35398-
// strictNullChecks otherwise.
35386+
// to be tested, we de-scope to functions and Promises unreferenced in
35387+
// the block as a heuristic to identify the most common bugs. There
35388+
// are too many false positives for values sourced from type
35389+
// definitions without strictNullChecks otherwise.
3539935390
const callSignatures = getSignaturesOfType(type, SignatureKind.Call);
35400-
if (callSignatures.length === 0) {
35391+
const isPromise = !!getAwaitedTypeOfPromise(type);
35392+
if (callSignatures.length === 0 && !isPromise) {
3540135393
return;
3540235394
}
3540335395

@@ -35406,14 +35398,23 @@ namespace ts {
3540635398
return;
3540735399
}
3540835400

35409-
const isUsed = isBinaryExpression(condExpr.parent) && isFunctionUsedInBinaryExpressionChain(condExpr.parent, testedSymbol)
35410-
|| body && isFunctionUsedInConditionBody(condExpr, body, testedNode, testedSymbol);
35401+
const isUsed = isBinaryExpression(condExpr.parent) && isSymbolUsedInBinaryExpressionChain(condExpr.parent, testedSymbol)
35402+
|| body && isSymbolUsedInConditionBody(condExpr, body, testedNode, testedSymbol);
3541135403
if (!isUsed) {
35412-
error(location, Diagnostics.This_condition_will_always_return_true_since_this_function_appears_to_always_be_defined_Did_you_mean_to_call_it_instead);
35404+
if (isPromise) {
35405+
errorAndMaybeSuggestAwait(
35406+
location,
35407+
/*maybeMissingAwait*/ true,
35408+
Diagnostics.This_condition_will_always_return_true_since_this_0_is_always_defined,
35409+
getTypeNameForErrorDisplay(type));
35410+
}
35411+
else {
35412+
error(location, Diagnostics.This_condition_will_always_return_true_since_this_function_is_always_defined_Did_you_mean_to_call_it_instead);
35413+
}
3541335414
}
3541435415
}
3541535416

35416-
function isFunctionUsedInConditionBody(expr: Expression, body: Statement | Expression, testedNode: Node, testedSymbol: Symbol): boolean {
35417+
function isSymbolUsedInConditionBody(expr: Expression, body: Statement | Expression, testedNode: Node, testedSymbol: Symbol): boolean {
3541735418
return !!forEachChild(body, function check(childNode): boolean | undefined {
3541835419
if (isIdentifier(childNode)) {
3541935420
const childSymbol = getSymbolAtLocation(childNode);
@@ -35451,7 +35452,7 @@ namespace ts {
3545135452
});
3545235453
}
3545335454

35454-
function isFunctionUsedInBinaryExpressionChain(node: Node, testedSymbol: Symbol): boolean {
35455+
function isSymbolUsedInBinaryExpressionChain(node: Node, testedSymbol: Symbol): boolean {
3545535456
while (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
3545635457
const isUsed = forEachChild(node.right, function visit(child): boolean | undefined {
3545735458
if (isIdentifier(child)) {

src/compiler/diagnosticMessages.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,7 +3172,7 @@
31723172
"category": "Error",
31733173
"code": 2773
31743174
},
3175-
"This condition will always return true since this function appears to always be defined. Did you mean to call it instead?": {
3175+
"This condition will always return true since this function is always defined. Did you mean to call it instead?": {
31763176
"category": "Error",
31773177
"code": 2774
31783178
},
@@ -3280,7 +3280,7 @@
32803280
"category": "Error",
32813281
"code": 2800
32823282
},
3283-
"This condition will always return true since this '{0}' appears to always be defined.": {
3283+
"This condition will always return true since this '{0}' is always defined.": {
32843284
"category": "Error",
32853285
"code": 2801
32863286
},

src/services/codefixes/addMissingAwait.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace ts.codefix {
1414
Diagnostics.Operator_0_cannot_be_applied_to_type_1.code,
1515
Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2.code,
1616
Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap.code,
17-
Diagnostics.This_condition_will_always_return_true_since_this_0_appears_to_always_be_defined.code,
17+
Diagnostics.This_condition_will_always_return_true_since_this_0_is_always_defined.code,
1818
Diagnostics.Type_0_is_not_an_array_type.code,
1919
Diagnostics.Type_0_is_not_an_array_type_or_a_string_type.code,
2020
Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators.code,

src/services/codefixes/fixMissingCallParentheses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
namespace ts.codefix {
33
const fixId = "fixMissingCallParentheses";
44
const errorCodes = [
5-
Diagnostics.This_condition_will_always_return_true_since_this_function_appears_to_always_be_defined_Did_you_mean_to_call_it_instead.code,
5+
Diagnostics.This_condition_will_always_return_true_since_this_function_is_always_defined_Did_you_mean_to_call_it_instead.code,
66
];
77

88
registerCodeFix({

tests/baselines/reference/truthinessCallExpressionCoercion.errors.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(2,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
2-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(18,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
3-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(36,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
4-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(50,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
5-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(66,13): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
6-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(76,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
7-
tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
1+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(2,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
2+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(18,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
3+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(36,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
4+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(50,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
5+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(66,13): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
6+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(76,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
7+
tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
88

99

1010
==== tests/cases/compiler/truthinessCallExpressionCoercion.ts (7 errors) ====
1111
function onlyErrorsWhenTestingNonNullableFunctionType(required: () => boolean, optional?: () => boolean) {
1212
if (required) { // error
1313
~~~~~~~~
14-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
14+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
1515
}
1616

1717
if (optional) { // ok
@@ -29,7 +29,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: Th
2929

3030
if (test) { // error
3131
~~~~
32-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
32+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
3333
console.log('test');
3434
}
3535

@@ -49,7 +49,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: Th
4949

5050
if (test) { // error
5151
~~~~
52-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
52+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
5353
[() => null].forEach(test => {
5454
test();
5555
});
@@ -65,7 +65,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: Th
6565

6666
if (x.foo.bar) { // error
6767
~~~~~~~~~
68-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
68+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
6969
}
7070

7171
if (x.foo.bar) { // ok
@@ -83,7 +83,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: Th
8383
test() {
8484
if (this.isUser) { // error
8585
~~~~~~~~~~~
86-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
86+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
8787
}
8888

8989
if (this.maybeIsUser) { // ok
@@ -95,15 +95,15 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: Th
9595
function A(stats: StatsBase<any>) {
9696
if (stats.isDirectory) { // err
9797
~~~~~~~~~~~~~~~~~
98-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
98+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
9999
console.log(`[Directory] ${stats.ctime}`)
100100
}
101101
}
102102

103103
function B(a: Nested, b: Nested) {
104104
if (a.stats.isDirectory) { // err
105105
~~~~~~~~~~~~~~~~~~~
106-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
106+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
107107
b.stats.isDirectory();
108108
}
109109
if (a.stats.isDirectory) { // ok

tests/baselines/reference/truthinessCallExpressionCoercion1.errors.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(3,5): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
2-
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(19,5): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
3-
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(33,5): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
4-
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(46,5): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
5-
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
1+
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(3,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
2+
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(19,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
3+
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(33,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
4+
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(46,5): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
5+
tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
66

77

88
==== tests/cases/compiler/truthinessCallExpressionCoercion1.ts (5 errors) ====
99
function onlyErrorsWhenTestingNonNullableFunctionType(required: () => boolean, optional?: () => boolean) {
1010
// error
1111
required ? console.log('required') : undefined;
1212
~~~~~~~~
13-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
13+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
1414

1515
// ok
1616
optional ? console.log('optional') : undefined;
@@ -28,7 +28,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: T
2828
// error
2929
test ? console.log('test') : undefined;
3030
~~~~
31-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
31+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
3232

3333
// ok
3434
test ? console.log(test) : undefined;
@@ -44,7 +44,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: T
4444
// error
4545
test
4646
~~~~
47-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
47+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
4848
? [() => null].forEach(test => { test() })
4949
: undefined;
5050
}
@@ -59,7 +59,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: T
5959
// error
6060
x.foo.bar ? console.log('x.foo.bar') : undefined;
6161
~~~~~~~~~
62-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
62+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
6363

6464
// ok
6565
x.foo.bar ? x.foo.bar : undefined;
@@ -91,7 +91,7 @@ tests/cases/compiler/truthinessCallExpressionCoercion1.ts(76,9): error TS2774: T
9191
// error
9292
this.isUser ? console.log('this.isUser') : undefined;
9393
~~~~~~~~~~~
94-
!!! error TS2774: This condition will always return true since this function appears to always be defined. Did you mean to call it instead?
94+
!!! error TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
9595

9696
// ok
9797
this.maybeIsUser ? console.log('this.maybeIsUser') : undefined;

0 commit comments

Comments
 (0)