Fixed issue where missing method call went unreported if the call target symbol did no have an id assigned or if the called property was used inside the if block on a different target. (#35862)

This commit is contained in:
Titian Cernicova-Dragomir 2020-03-31 00:45:32 +03:00 committed by GitHub
parent 0e15b9f245
commit 3433434142
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 268 additions and 5 deletions

View File

@ -31620,8 +31620,31 @@ namespace ts {
const functionIsUsedInBody = forEachChild(body, function check(childNode): boolean | undefined {
if (isIdentifier(childNode)) {
const childSymbol = getSymbolAtLocation(childNode);
if (childSymbol && childSymbol.id === testedFunctionSymbol.id) {
return true;
if (childSymbol && childSymbol === testedFunctionSymbol) {
// If the test was a simple identifier, the above check is sufficient
if (isIdentifier(ifStatement.expression)) {
return true;
}
// Otherwise we need to ensure the symbol is called on the same target
let testedExpression = testedNode.parent;
let childExpression = childNode.parent;
while (testedExpression && childExpression) {
if (isIdentifier(testedExpression) && isIdentifier(childExpression)) {
return getSymbolAtLocation(testedExpression) === getSymbolAtLocation(childExpression);
}
if (isPropertyAccessExpression(testedExpression) && isPropertyAccessExpression(childExpression)) {
if (getSymbolAtLocation(testedExpression.name) !== getSymbolAtLocation(childExpression.name)) {
return false;
}
childExpression = childExpression.expression;
testedExpression = testedExpression.expression;
}
else {
return false;
}
}
}
}

View File

@ -3,9 +3,11 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(18,9): error TS2774: Th
tests/cases/compiler/truthinessCallExpressionCoercion.ts(36,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
tests/cases/compiler/truthinessCallExpressionCoercion.ts(50,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
tests/cases/compiler/truthinessCallExpressionCoercion.ts(66,13): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
tests/cases/compiler/truthinessCallExpressionCoercion.ts(76,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
tests/cases/compiler/truthinessCallExpressionCoercion.ts(82,9): error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
==== tests/cases/compiler/truthinessCallExpressionCoercion.ts (5 errors) ====
==== tests/cases/compiler/truthinessCallExpressionCoercion.ts (7 errors) ====
function onlyErrorsWhenTestingNonNullableFunctionType(required: () => boolean, optional?: () => boolean) {
if (required) { // error
~~~~~~~~
@ -88,4 +90,32 @@ tests/cases/compiler/truthinessCallExpressionCoercion.ts(66,13): error TS2774: T
}
}
}
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats: StatsBase<any>) {
if (stats.isDirectory) { // err
~~~~~~~~~~~~~~~~~
!!! error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
console.log(`[Directory] ${stats.ctime}`)
}
}
function B(a: Nested, b: Nested) {
if (a.stats.isDirectory) { // err
~~~~~~~~~~~~~~~~~~~
!!! error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
b.stats.isDirectory();
}
if (a.stats.isDirectory) { // ok
a.stats.isDirectory();
}
}
interface StatsBase<T> {
isDirectory(): boolean;
ctime: number;
}
interface Nested {
stats: StatsBase<any>;
}

View File

@ -71,7 +71,31 @@ class Foo {
}
}
}
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats: StatsBase<any>) {
if (stats.isDirectory) { // err
console.log(`[Directory] ${stats.ctime}`)
}
}
function B(a: Nested, b: Nested) {
if (a.stats.isDirectory) { // err
b.stats.isDirectory();
}
if (a.stats.isDirectory) { // ok
a.stats.isDirectory();
}
}
interface StatsBase<T> {
isDirectory(): boolean;
ctime: number;
}
interface Nested {
stats: StatsBase<any>;
}
//// [truthinessCallExpressionCoercion.js]
function onlyErrorsWhenTestingNonNullableFunctionType(required, optional) {
@ -132,3 +156,17 @@ var Foo = /** @class */ (function () {
};
return Foo;
}());
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats) {
if (stats.isDirectory) { // err
console.log("[Directory] " + stats.ctime);
}
}
function B(a, b) {
if (a.stats.isDirectory) { // err
b.stats.isDirectory();
}
if (a.stats.isDirectory) { // ok
a.stats.isDirectory();
}
}

View File

@ -151,3 +151,79 @@ class Foo {
}
}
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats: StatsBase<any>) {
>A : Symbol(A, Decl(truthinessCallExpressionCoercion.ts, 71, 1))
>stats : Symbol(stats, Decl(truthinessCallExpressionCoercion.ts, 74, 11))
>StatsBase : Symbol(StatsBase, Decl(truthinessCallExpressionCoercion.ts, 87, 1))
if (stats.isDirectory) { // err
>stats.isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
>stats : Symbol(stats, Decl(truthinessCallExpressionCoercion.ts, 74, 11))
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
console.log(`[Directory] ${stats.ctime}`)
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>stats.ctime : Symbol(StatsBase.ctime, Decl(truthinessCallExpressionCoercion.ts, 90, 27))
>stats : Symbol(stats, Decl(truthinessCallExpressionCoercion.ts, 74, 11))
>ctime : Symbol(StatsBase.ctime, Decl(truthinessCallExpressionCoercion.ts, 90, 27))
}
}
function B(a: Nested, b: Nested) {
>B : Symbol(B, Decl(truthinessCallExpressionCoercion.ts, 78, 1))
>a : Symbol(a, Decl(truthinessCallExpressionCoercion.ts, 80, 11))
>Nested : Symbol(Nested, Decl(truthinessCallExpressionCoercion.ts, 92, 1))
>b : Symbol(b, Decl(truthinessCallExpressionCoercion.ts, 80, 21))
>Nested : Symbol(Nested, Decl(truthinessCallExpressionCoercion.ts, 92, 1))
if (a.stats.isDirectory) { // err
>a.stats.isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
>a.stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>a : Symbol(a, Decl(truthinessCallExpressionCoercion.ts, 80, 11))
>stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
b.stats.isDirectory();
>b.stats.isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
>b.stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>b : Symbol(b, Decl(truthinessCallExpressionCoercion.ts, 80, 21))
>stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
}
if (a.stats.isDirectory) { // ok
>a.stats.isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
>a.stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>a : Symbol(a, Decl(truthinessCallExpressionCoercion.ts, 80, 11))
>stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
a.stats.isDirectory();
>a.stats.isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
>a.stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>a : Symbol(a, Decl(truthinessCallExpressionCoercion.ts, 80, 11))
>stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
}
}
interface StatsBase<T> {
>StatsBase : Symbol(StatsBase, Decl(truthinessCallExpressionCoercion.ts, 87, 1))
>T : Symbol(T, Decl(truthinessCallExpressionCoercion.ts, 89, 20))
isDirectory(): boolean;
>isDirectory : Symbol(StatsBase.isDirectory, Decl(truthinessCallExpressionCoercion.ts, 89, 24))
ctime: number;
>ctime : Symbol(StatsBase.ctime, Decl(truthinessCallExpressionCoercion.ts, 90, 27))
}
interface Nested {
>Nested : Symbol(Nested, Decl(truthinessCallExpressionCoercion.ts, 92, 1))
stats: StatsBase<any>;
>stats : Symbol(Nested.stats, Decl(truthinessCallExpressionCoercion.ts, 94, 18))
>StatsBase : Symbol(StatsBase, Decl(truthinessCallExpressionCoercion.ts, 87, 1))
}

View File

@ -177,3 +177,74 @@ class Foo {
}
}
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats: StatsBase<any>) {
>A : (stats: StatsBase<any>) => void
>stats : StatsBase<any>
if (stats.isDirectory) { // err
>stats.isDirectory : () => boolean
>stats : StatsBase<any>
>isDirectory : () => boolean
console.log(`[Directory] ${stats.ctime}`)
>console.log(`[Directory] ${stats.ctime}`) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>`[Directory] ${stats.ctime}` : string
>stats.ctime : number
>stats : StatsBase<any>
>ctime : number
}
}
function B(a: Nested, b: Nested) {
>B : (a: Nested, b: Nested) => void
>a : Nested
>b : Nested
if (a.stats.isDirectory) { // err
>a.stats.isDirectory : () => boolean
>a.stats : StatsBase<any>
>a : Nested
>stats : StatsBase<any>
>isDirectory : () => boolean
b.stats.isDirectory();
>b.stats.isDirectory() : boolean
>b.stats.isDirectory : () => boolean
>b.stats : StatsBase<any>
>b : Nested
>stats : StatsBase<any>
>isDirectory : () => boolean
}
if (a.stats.isDirectory) { // ok
>a.stats.isDirectory : () => boolean
>a.stats : StatsBase<any>
>a : Nested
>stats : StatsBase<any>
>isDirectory : () => boolean
a.stats.isDirectory();
>a.stats.isDirectory() : boolean
>a.stats.isDirectory : () => boolean
>a.stats : StatsBase<any>
>a : Nested
>stats : StatsBase<any>
>isDirectory : () => boolean
}
}
interface StatsBase<T> {
isDirectory(): boolean;
>isDirectory : () => boolean
ctime: number;
>ctime : number
}
interface Nested {
stats: StatsBase<any>;
>stats : StatsBase<any>
}

View File

@ -72,3 +72,28 @@ class Foo {
}
}
}
// Test for GH-35557 where ids were not assigned for a symbol.
function A(stats: StatsBase<any>) {
if (stats.isDirectory) { // err
console.log(`[Directory] ${stats.ctime}`)
}
}
function B(a: Nested, b: Nested) {
if (a.stats.isDirectory) { // err
b.stats.isDirectory();
}
if (a.stats.isDirectory) { // ok
a.stats.isDirectory();
}
}
interface StatsBase<T> {
isDirectory(): boolean;
ctime: number;
}
interface Nested {
stats: StatsBase<any>;
}