Include super.XXX(...) assertion method calls in CFA (#36293)

* Support super.XXX in assertions

* Add tests
This commit is contained in:
Anders Hejlsberg
2020-01-22 11:21:11 -08:00
committed by GitHub
parent c8e2f58ec5
commit da61231039
12 changed files with 337 additions and 47 deletions
+2
View File
@@ -19075,6 +19075,8 @@ namespace ts {
return getExplicitTypeOfSymbol(symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol, diagnostic);
case SyntaxKind.ThisKeyword:
return getExplicitThisType(node);
case SyntaxKind.SuperKeyword:
return checkSuperExpression(node);
case SyntaxKind.PropertyAccessExpression:
const type = getTypeOfDottedName((<PropertyAccessExpression>node).expression, diagnostic);
const prop = type && getPropertyOfType(type, (<PropertyAccessExpression>node).name.escapedText);
+1 -1
View File
@@ -4278,7 +4278,7 @@ namespace ts {
}
export function isDottedName(node: Expression): boolean {
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword ||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword ||
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) ||
node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((<ParenthesizedExpression>node).expression);
}
@@ -2,19 +2,20 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(39,9): error TS7
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(43,9): error TS7027: Unreachable code detected.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(87,9): error TS7027: Unreachable code detected.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(122,9): error TS7027: Unreachable code detected.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(132,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(133,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(134,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(137,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(138,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(139,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(140,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(145,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(147,5): error TS2776: Assertions require the call target to be an identifier or qualified name.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(137,9): error TS7027: Unreachable code detected.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(143,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(144,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(145,37): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(148,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(150,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(151,15): error TS1228: A type predicate is only allowed in return type position for functions and methods.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(156,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(158,5): error TS2776: Assertions require the call target to be an identifier or qualified name.
tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(160,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
==== tests/cases/conformance/controlFlow/assertionTypePredicates1.ts (14 errors) ====
==== tests/cases/conformance/controlFlow/assertionTypePredicates1.ts (15 errors) ====
declare function isString(value: unknown): value is string;
declare function isArrayOfStrings(value: unknown): value is string[];
@@ -152,6 +153,19 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,5): error TS
z = 0;
}
class Derived extends Test {
foo(x: unknown) {
super.assert(typeof x === "string");
x.length;
}
baz(x: number) {
super.assert(false);
x; // Unreachable
~~
!!! error TS7027: Unreachable code detected.
}
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
@@ -184,7 +198,7 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,5): error TS
assert(typeof x === "string"); // Error
~~~~~~
!!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:144:11: 'assert' needs an explicit type annotation.
!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:155:11: 'assert' needs an explicit type annotation.
const a = [assert];
a[0](typeof x === "string"); // Error
~~~~
@@ -193,7 +207,7 @@ tests/cases/conformance/controlFlow/assertionTypePredicates1.ts(149,5): error TS
t1.assert(typeof x === "string"); // Error
~~~~~~~~~
!!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:148:11: 't1' needs an explicit type annotation.
!!! related TS2782 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:159:11: 't1' needs an explicit type annotation.
const t2: Test = new Test();
t2.assert(typeof x === "string");
}
@@ -128,6 +128,17 @@ class Test2 extends Test {
z = 0;
}
class Derived extends Test {
foo(x: unknown) {
super.assert(typeof x === "string");
x.length;
}
baz(x: number) {
super.assert(false);
x; // Unreachable
}
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
@@ -293,6 +304,21 @@ var Test2 = /** @class */ (function (_super) {
}
return Test2;
}(Test));
var Derived = /** @class */ (function (_super) {
__extends(Derived, _super);
function Derived() {
return _super !== null && _super.apply(this, arguments) || this;
}
Derived.prototype.foo = function (x) {
_super.prototype.assert.call(this, typeof x === "string");
x.length;
};
Derived.prototype.baz = function (x) {
_super.prototype.assert.call(this, false);
x; // Unreachable
};
return Derived;
}(Test));
function f20(x) {
var assert = function (value) { };
assert(typeof x === "string"); // Error
@@ -332,6 +358,10 @@ declare class Test {
declare class Test2 extends Test {
z: number;
}
declare class Derived extends Test {
foo(x: unknown): void;
baz(x: number): void;
}
declare let Q1: new (x: unknown) => x is string;
declare let Q2: new (x: boolean) => asserts x;
declare let Q3: new (x: unknown) => asserts x is string;
@@ -359,81 +359,114 @@ class Test2 extends Test {
>z : Symbol(Test2.z, Decl(assertionTypePredicates1.ts, 125, 26))
}
class Derived extends Test {
>Derived : Symbol(Derived, Decl(assertionTypePredicates1.ts, 127, 1))
>Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
foo(x: unknown) {
>foo : Symbol(Derived.foo, Decl(assertionTypePredicates1.ts, 129, 28))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 130, 8))
super.assert(typeof x === "string");
>super.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>super : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
>assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 130, 8))
x.length;
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 130, 8))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}
baz(x: number) {
>baz : Symbol(Derived.baz, Decl(assertionTypePredicates1.ts, 133, 5))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 134, 8))
super.assert(false);
>super.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>super : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
>assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
x; // Unreachable
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 134, 8))
}
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
>Q1 : Symbol(Q1, Decl(assertionTypePredicates1.ts, 131, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 131, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 131, 21))
>Q1 : Symbol(Q1, Decl(assertionTypePredicates1.ts, 142, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 21))
declare let Q2: new (x: boolean) => asserts x;
>Q2 : Symbol(Q2, Decl(assertionTypePredicates1.ts, 132, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 132, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 132, 21))
>Q2 : Symbol(Q2, Decl(assertionTypePredicates1.ts, 143, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 143, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 143, 21))
declare let Q3: new (x: unknown) => asserts x is string;
>Q3 : Symbol(Q3, Decl(assertionTypePredicates1.ts, 133, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 133, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 133, 21))
>Q3 : Symbol(Q3, Decl(assertionTypePredicates1.ts, 144, 11))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 144, 21))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 144, 21))
declare class Wat {
>Wat : Symbol(Wat, Decl(assertionTypePredicates1.ts, 133, 56))
>Wat : Symbol(Wat, Decl(assertionTypePredicates1.ts, 144, 56))
get p1(): this is string;
>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 135, 19), Decl(assertionTypePredicates1.ts, 136, 29))
>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 146, 19), Decl(assertionTypePredicates1.ts, 147, 29))
set p1(x: this is string);
>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 135, 19), Decl(assertionTypePredicates1.ts, 136, 29))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 137, 11))
>p1 : Symbol(Wat.p1, Decl(assertionTypePredicates1.ts, 146, 19), Decl(assertionTypePredicates1.ts, 147, 29))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 148, 11))
get p2(): asserts this is string;
>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 137, 30), Decl(assertionTypePredicates1.ts, 138, 37))
>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 148, 30), Decl(assertionTypePredicates1.ts, 149, 37))
set p2(x: asserts this is string);
>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 137, 30), Decl(assertionTypePredicates1.ts, 138, 37))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 139, 11))
>p2 : Symbol(Wat.p2, Decl(assertionTypePredicates1.ts, 148, 30), Decl(assertionTypePredicates1.ts, 149, 37))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 150, 11))
}
function f20(x: unknown) {
>f20 : Symbol(f20, Decl(assertionTypePredicates1.ts, 140, 1))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 13))
>f20 : Symbol(f20, Decl(assertionTypePredicates1.ts, 151, 1))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13))
const assert = (value: unknown): asserts value => {}
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 143, 9))
>value : Symbol(value, Decl(assertionTypePredicates1.ts, 143, 20))
>value : Symbol(value, Decl(assertionTypePredicates1.ts, 143, 20))
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9))
>value : Symbol(value, Decl(assertionTypePredicates1.ts, 154, 20))
>value : Symbol(value, Decl(assertionTypePredicates1.ts, 154, 20))
assert(typeof x === "string"); // Error
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 143, 9))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 13))
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13))
const a = [assert];
>a : Symbol(a, Decl(assertionTypePredicates1.ts, 145, 9))
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 143, 9))
>a : Symbol(a, Decl(assertionTypePredicates1.ts, 156, 9))
>assert : Symbol(assert, Decl(assertionTypePredicates1.ts, 154, 9))
a[0](typeof x === "string"); // Error
>a : Symbol(a, Decl(assertionTypePredicates1.ts, 145, 9))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 13))
>a : Symbol(a, Decl(assertionTypePredicates1.ts, 156, 9))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13))
const t1 = new Test();
>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 147, 9))
>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 158, 9))
>Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
t1.assert(typeof x === "string"); // Error
>t1.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 147, 9))
>t1 : Symbol(t1, Decl(assertionTypePredicates1.ts, 158, 9))
>assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 13))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13))
const t2: Test = new Test();
>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 149, 9))
>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 160, 9))
>Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
>Test : Symbol(Test, Decl(assertionTypePredicates1.ts, 88, 1))
t2.assert(typeof x === "string");
>t2.assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 149, 9))
>t2 : Symbol(t2, Decl(assertionTypePredicates1.ts, 160, 9))
>assert : Symbol(Test.assert, Decl(assertionTypePredicates1.ts, 90, 12))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 142, 13))
>x : Symbol(x, Decl(assertionTypePredicates1.ts, 153, 13))
}
@@ -463,6 +463,45 @@ class Test2 extends Test {
>0 : 0
}
class Derived extends Test {
>Derived : Derived
>Test : Test
foo(x: unknown) {
>foo : (x: unknown) => void
>x : unknown
super.assert(typeof x === "string");
>super.assert(typeof x === "string") : void
>super.assert : (value: unknown) => asserts value
>super : Test
>assert : (value: unknown) => asserts value
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>"string" : "string"
x.length;
>x.length : number
>x : string
>length : number
}
baz(x: number) {
>baz : (x: number) => void
>x : number
super.assert(false);
>super.assert(false) : void
>super.assert : (value: unknown) => asserts value
>super : Test
>assert : (value: unknown) => asserts value
>false : false
x; // Unreachable
>x : number
}
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
@@ -301,4 +301,21 @@ tests/cases/conformance/controlFlow/neverReturningFunctions1.ts(153,5): error TS
return f * this.data.num * this.system!.data.counter;
}
});
// Repro from #36147
class MyThrowable {
throw(): never {
throw new Error();
}
}
class SuperThrowable extends MyThrowable {
err(msg: string): never {
super.throw()
}
ok(): never {
this.throw()
}
}
@@ -230,10 +230,40 @@ const Component = registerComponent('test-component', {
return f * this.data.num * this.system!.data.counter;
}
});
// Repro from #36147
class MyThrowable {
throw(): never {
throw new Error();
}
}
class SuperThrowable extends MyThrowable {
err(msg: string): never {
super.throw()
}
ok(): never {
this.throw()
}
}
//// [neverReturningFunctions1.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
function fail(message) {
throw new Error(message);
@@ -415,6 +445,28 @@ var Component = registerComponent('test-component', {
return f * this.data.num * this.system.data.counter;
}
});
// Repro from #36147
var MyThrowable = /** @class */ (function () {
function MyThrowable() {
}
MyThrowable.prototype["throw"] = function () {
throw new Error();
};
return MyThrowable;
}());
var SuperThrowable = /** @class */ (function (_super) {
__extends(SuperThrowable, _super);
function SuperThrowable() {
return _super !== null && _super.apply(this, arguments) || this;
}
SuperThrowable.prototype.err = function (msg) {
_super.prototype["throw"].call(this);
};
SuperThrowable.prototype.ok = function () {
this["throw"]();
};
return SuperThrowable;
}(MyThrowable));
//// [neverReturningFunctions1.d.ts]
@@ -599,3 +599,39 @@ const Component = registerComponent('test-component', {
}
});
// Repro from #36147
class MyThrowable {
>MyThrowable : Symbol(MyThrowable, Decl(neverReturningFunctions1.ts, 230, 3))
throw(): never {
>throw : Symbol(MyThrowable.throw, Decl(neverReturningFunctions1.ts, 234, 19))
throw new Error();
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
}
class SuperThrowable extends MyThrowable {
>SuperThrowable : Symbol(SuperThrowable, Decl(neverReturningFunctions1.ts, 238, 1))
>MyThrowable : Symbol(MyThrowable, Decl(neverReturningFunctions1.ts, 230, 3))
err(msg: string): never {
>err : Symbol(SuperThrowable.err, Decl(neverReturningFunctions1.ts, 240, 42))
>msg : Symbol(msg, Decl(neverReturningFunctions1.ts, 241, 8))
super.throw()
>super.throw : Symbol(MyThrowable.throw, Decl(neverReturningFunctions1.ts, 234, 19))
>super : Symbol(MyThrowable, Decl(neverReturningFunctions1.ts, 230, 3))
>throw : Symbol(MyThrowable.throw, Decl(neverReturningFunctions1.ts, 234, 19))
}
ok(): never {
>ok : Symbol(SuperThrowable.ok, Decl(neverReturningFunctions1.ts, 243, 5))
this.throw()
>this.throw : Symbol(MyThrowable.throw, Decl(neverReturningFunctions1.ts, 234, 19))
>this : Symbol(SuperThrowable, Decl(neverReturningFunctions1.ts, 238, 1))
>throw : Symbol(MyThrowable.throw, Decl(neverReturningFunctions1.ts, 234, 19))
}
}
@@ -667,3 +667,42 @@ const Component = registerComponent('test-component', {
}
});
// Repro from #36147
class MyThrowable {
>MyThrowable : MyThrowable
throw(): never {
>throw : () => never
throw new Error();
>new Error() : Error
>Error : ErrorConstructor
}
}
class SuperThrowable extends MyThrowable {
>SuperThrowable : SuperThrowable
>MyThrowable : MyThrowable
err(msg: string): never {
>err : (msg: string) => never
>msg : string
super.throw()
>super.throw() : never
>super.throw : () => never
>super : MyThrowable
>throw : () => never
}
ok(): never {
>ok : () => never
this.throw()
>this.throw() : never
>this.throw : () => never
>this : this
>throw : () => never
}
}
@@ -131,6 +131,17 @@ class Test2 extends Test {
z = 0;
}
class Derived extends Test {
foo(x: unknown) {
super.assert(typeof x === "string");
x.length;
}
baz(x: number) {
super.assert(false);
x; // Unreachable
}
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
@@ -233,3 +233,20 @@ const Component = registerComponent('test-component', {
return f * this.data.num * this.system!.data.counter;
}
});
// Repro from #36147
class MyThrowable {
throw(): never {
throw new Error();
}
}
class SuperThrowable extends MyThrowable {
err(msg: string): never {
super.throw()
}
ok(): never {
this.throw()
}
}