Improve uncalled function checks (#41599)

Fixes #41586
Fixes #41588

1. For binary expressions, if the immediate parent is an IfStatement,
then check the body of the if statement. I didn't walk upward to find an
IfStatement because in my experimentation I found that binary expression
uncalled-function errors are only issued when the expression is on the left of the
top-most binary expression.

2. For property accesses with interspersed calls, I added a
CallExpression case. In fact, any expression could appear here, but I
only want to fix calls for now since that's all we've observed in
Definitely Typed, and we didn't see anything else in the user tests or RWC
tests. I also didn't examine parameters of the intermediate call
expressions, but I don't think it's needed since the intent is to avoid
false positives.
This commit is contained in:
Nathan Shively-Sanders
2020-11-30 14:27:19 -08:00
committed by GitHub
parent 23b3eb685f
commit 06fb724cd1
11 changed files with 338 additions and 125 deletions

View File

@@ -30185,7 +30185,7 @@ namespace ts {
const operator = node.operatorToken.kind;
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) {
if (operator === SyntaxKind.AmpersandAmpersandToken) {
checkTestingKnownTruthyCallableType(node.left, leftType);
checkTestingKnownTruthyCallableType(node.left, leftType, isIfStatement(node.parent) ? node.parent.thenStatement : undefined);
}
checkTruthinessOfType(leftType, node.left);
}
@@ -33972,7 +33972,7 @@ namespace ts {
checkSourceElement(node.elseStatement);
}
function checkTestingKnownTruthyCallableType(condExpr: Expression, type: Type, body?: Statement | Expression) {
function checkTestingKnownTruthyCallableType(condExpr: Expression, type: Type, body: Statement | Expression | undefined) {
if (!strictNullChecks) {
return;
}
@@ -34007,9 +34007,8 @@ namespace ts {
return;
}
const isUsed = isBinaryExpression(condExpr.parent) ? isFunctionUsedInBinaryExpressionChain(condExpr.parent, testedSymbol)
: body ? isFunctionUsedInConditionBody(condExpr, body, testedNode, testedSymbol)
: false;
const isUsed = isBinaryExpression(condExpr.parent) && isFunctionUsedInBinaryExpressionChain(condExpr.parent, testedSymbol)
|| body && isFunctionUsedInConditionBody(condExpr, body, testedNode, testedSymbol);
if (!isUsed) {
error(location, Diagnostics.This_condition_will_always_return_true_since_the_function_is_always_defined_Did_you_mean_to_call_it_instead);
}
@@ -34032,14 +34031,17 @@ namespace ts {
testedExpression.kind === SyntaxKind.ThisKeyword && childExpression.kind === SyntaxKind.ThisKeyword) {
return getSymbolAtLocation(testedExpression) === getSymbolAtLocation(childExpression);
}
if (isPropertyAccessExpression(testedExpression) && isPropertyAccessExpression(childExpression)) {
else if (isPropertyAccessExpression(testedExpression) && isPropertyAccessExpression(childExpression)) {
if (getSymbolAtLocation(testedExpression.name) !== getSymbolAtLocation(childExpression.name)) {
return false;
}
childExpression = childExpression.expression;
testedExpression = testedExpression.expression;
}
else if (isCallExpression(testedExpression) && isCallExpression(childExpression)) {
childExpression = childExpression.expression;
testedExpression = testedExpression.expression;
}
else {
return false;
}