From f3e4ba459462a5bed783dc14d7295ce2b3927324 Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Fri, 3 Jul 2020 01:16:28 +0800 Subject: [PATCH] switch typeof any could be checked for unreachable --- src/compiler/checker.ts | 17 ++++--- .../unreachableSwitchTypeofAny.errors.txt | 19 ++++++++ .../reference/unreachableSwitchTypeofAny.js | 29 ++++++++++++ .../unreachableSwitchTypeofAny.symbols | 20 +++++++++ .../unreachableSwitchTypeofAny.types | 45 +++++++++++++++++++ .../compiler/unreachableSwitchTypeofAny.ts | 16 +++++++ 6 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt create mode 100644 tests/baselines/reference/unreachableSwitchTypeofAny.js create mode 100644 tests/baselines/reference/unreachableSwitchTypeofAny.symbols create mode 100644 tests/baselines/reference/unreachableSwitchTypeofAny.types create mode 100644 tests/cases/compiler/unreachableSwitchTypeofAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30613eac51519..6c5ddeda98f13 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -131,6 +131,7 @@ namespace ts { UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull), + AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, EmptyObjectFacts = All, } @@ -28565,6 +28566,10 @@ namespace ts { // notEqualFacts states that the type of the switched value is not equal to every type in the switch. const notEqualFacts = getFactsFromTypeofSwitch(0, 0, witnesses, /*hasDefault*/ true); const type = getBaseConstraintOfType(operandType) || operandType; + // Take any as a specail condition. Maybe we could change type to a union containing all primitive types. + if (type.flags = TypeFlags.Any) { + return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; + } return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); } const type = getTypeOfExpression(node.expression); @@ -30345,12 +30350,12 @@ namespace ts { } // If a type has been cached for the node, return it. // Note: this is not only cache, without this, some test case would always runs, such as binaryArithmeticControlFlowGraphNotTooLarge. - if (node.flags & NodeFlags.TypeCached && flowTypeCache) { - const cachedType = flowTypeCache[getNodeId(node)]; - if (cachedType) { - return cachedType; - } - } + // if (node.flags & NodeFlags.TypeCached && flowTypeCache) { + // const cachedType = flowTypeCache[getNodeId(node)]; + // if (cachedType) { + // return cachedType; + // } + // } const startInvocationCount = flowInvocationCount; const type = checkExpression(node); // If control flow analysis was required to determine the type, it is worth caching. diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt b/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt new file mode 100644 index 0000000000000..0b6b15ea49559 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/unreachable.ts(12,5): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/unreachable.ts (1 errors) ==== + const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; + ~~ +!!! error TS7027: Unreachable code detected. + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.js b/tests/baselines/reference/unreachableSwitchTypeofAny.js new file mode 100644 index 0000000000000..288c4d458caed --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.js @@ -0,0 +1,29 @@ +//// [unreachable.ts] +const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} + +//// [unreachable.js] +var unreachable = function (x) { + switch (typeof x) { + case 'string': return 0; + case 'number': return 0; + case 'bigint': return 0; + case 'boolean': return 0; + case 'symbol': return 0; + case 'undefined': return 0; + case 'object': return 0; + case 'function': return 0; + } + x; +}; diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.symbols b/tests/baselines/reference/unreachableSwitchTypeofAny.symbols new file mode 100644 index 0000000000000..9052798faf790 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: any): number => { +>unreachable : Symbol(unreachable, Decl(unreachable.ts, 0, 5)) +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + switch (typeof x) { +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) + + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +>x : Symbol(x, Decl(unreachable.ts, 0, 21)) +} diff --git a/tests/baselines/reference/unreachableSwitchTypeofAny.types b/tests/baselines/reference/unreachableSwitchTypeofAny.types new file mode 100644 index 0000000000000..8846c829ec5a8 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofAny.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: any): number => { +>unreachable : (x: any) => number +>(x: any): number => { switch (typeof x) { case 'string': return 0 case 'number': return 0 case 'bigint': return 0 case 'boolean': return 0 case 'symbol': return 0 case 'undefined': return 0 case 'object': return 0 case 'function': return 0 } x;} : (x: any) => number +>x : any + + switch (typeof x) { +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : any + + case 'string': return 0 +>'string' : "string" +>0 : 0 + + case 'number': return 0 +>'number' : "number" +>0 : 0 + + case 'bigint': return 0 +>'bigint' : "bigint" +>0 : 0 + + case 'boolean': return 0 +>'boolean' : "boolean" +>0 : 0 + + case 'symbol': return 0 +>'symbol' : "symbol" +>0 : 0 + + case 'undefined': return 0 +>'undefined' : "undefined" +>0 : 0 + + case 'object': return 0 +>'object' : "object" +>0 : 0 + + case 'function': return 0 +>'function' : "function" +>0 : 0 + } + x; +>x : any +} diff --git a/tests/cases/compiler/unreachableSwitchTypeofAny.ts b/tests/cases/compiler/unreachableSwitchTypeofAny.ts new file mode 100644 index 0000000000000..2cc5b86e59bda --- /dev/null +++ b/tests/cases/compiler/unreachableSwitchTypeofAny.ts @@ -0,0 +1,16 @@ +// @Filename: unreachable.ts +// @outDir: out +// @allowUnreachableCode: false +const unreachable = (x: any): number => { + switch (typeof x) { + case 'string': return 0 + case 'number': return 0 + case 'bigint': return 0 + case 'boolean': return 0 + case 'symbol': return 0 + case 'undefined': return 0 + case 'object': return 0 + case 'function': return 0 + } + x; +} \ No newline at end of file