From 92cdf0fc26c935244592314d4e0bca02a51c5569 Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Fri, 3 Jul 2020 01:16:28 +0800 Subject: [PATCH 1/5] switch typeof any could be checked for unreachable --- src/compiler/checker.ts | 5 +++ .../unreachableSwitchTypeofAny.errors.txt | 19 ++++++++ .../reference/unreachableSwitchTypeofAny.js | 29 ++++++++++++ .../unreachableSwitchTypeofAny.symbols | 20 +++++++++ .../unreachableSwitchTypeofAny.types | 45 +++++++++++++++++++ .../compiler/unreachableSwitchTypeofAny.ts | 16 +++++++ 6 files changed, 134 insertions(+) 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 aa0091f99523a..eff5d31f7a9c6 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, } @@ -28194,6 +28195,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); 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 From e317a35265667e40bf8fc963e5459b1803ea202a Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Fri, 3 Jul 2020 20:05:20 +0800 Subject: [PATCH 2/5] fix stupid error --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index eff5d31f7a9c6..5b59b28042af8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28196,7 +28196,7 @@ namespace ts { 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) { + if (type.flags === TypeFlags.Any) { return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; } return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); From 9b5f6bdd38b40a0b25a8e2e6599a02c542cefe93 Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Mon, 6 Jul 2020 17:56:14 +0800 Subject: [PATCH 3/5] support unknown --- src/compiler/checker.ts | 2 +- .../unreachableSwitchTypeofUnknown.errors.txt | 19 ++++++++ .../unreachableSwitchTypeofUnknown.js | 29 ++++++++++++ .../unreachableSwitchTypeofUnknown.symbols | 20 +++++++++ .../unreachableSwitchTypeofUnknown.types | 45 +++++++++++++++++++ .../unreachableSwitchTypeofUnknown.ts | 16 +++++++ 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt create mode 100644 tests/baselines/reference/unreachableSwitchTypeofUnknown.js create mode 100644 tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols create mode 100644 tests/baselines/reference/unreachableSwitchTypeofUnknown.types create mode 100644 tests/cases/compiler/unreachableSwitchTypeofUnknown.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5b59b28042af8..30bb33e52fd1a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28196,7 +28196,7 @@ namespace ts { 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) { + if (type.flags & TypeFlags.AnyOrUnknown) { return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; } return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never); diff --git a/tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt b/tests/baselines/reference/unreachableSwitchTypeofUnknown.errors.txt new file mode 100644 index 0000000000000..324e97d441f69 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.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: unknown): 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/unreachableSwitchTypeofUnknown.js b/tests/baselines/reference/unreachableSwitchTypeofUnknown.js new file mode 100644 index 0000000000000..3e476413ade04 --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.js @@ -0,0 +1,29 @@ +//// [unreachable.ts] +const unreachable = (x: unknown): 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/unreachableSwitchTypeofUnknown.symbols b/tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols new file mode 100644 index 0000000000000..984583c2163cb --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: unknown): 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/unreachableSwitchTypeofUnknown.types b/tests/baselines/reference/unreachableSwitchTypeofUnknown.types new file mode 100644 index 0000000000000..55d77b66196fa --- /dev/null +++ b/tests/baselines/reference/unreachableSwitchTypeofUnknown.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unreachable.ts === +const unreachable = (x: unknown): number => { +>unreachable : (x: unknown) => number +>(x: unknown): 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: unknown) => number +>x : unknown + + switch (typeof x) { +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : unknown + + 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 : unknown +} diff --git a/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts new file mode 100644 index 0000000000000..904eaf5b38bc3 --- /dev/null +++ b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts @@ -0,0 +1,16 @@ +// @Filename: unreachable.ts +// @outDir: out +// @allowUnreachableCode: false +const unreachable = (x: unknown): 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 From 60ffcd7a04e5b0d12915c0b671ce22137288eba2 Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Mon, 6 Jul 2020 21:41:51 +0800 Subject: [PATCH 4/5] remvoe use less code. --- tests/cases/compiler/unreachableSwitchTypeofAny.ts | 1 - tests/cases/compiler/unreachableSwitchTypeofUnknown.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/cases/compiler/unreachableSwitchTypeofAny.ts b/tests/cases/compiler/unreachableSwitchTypeofAny.ts index 2cc5b86e59bda..e230434941b5c 100644 --- a/tests/cases/compiler/unreachableSwitchTypeofAny.ts +++ b/tests/cases/compiler/unreachableSwitchTypeofAny.ts @@ -1,5 +1,4 @@ // @Filename: unreachable.ts -// @outDir: out // @allowUnreachableCode: false const unreachable = (x: any): number => { switch (typeof x) { diff --git a/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts index 904eaf5b38bc3..a2ec241bbdbc2 100644 --- a/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts +++ b/tests/cases/compiler/unreachableSwitchTypeofUnknown.ts @@ -1,5 +1,4 @@ // @Filename: unreachable.ts -// @outDir: out // @allowUnreachableCode: false const unreachable = (x: unknown): number => { switch (typeof x) { From 07b9bc51e9cfdce5a519de3c4f3367aa36150d51 Mon Sep 17 00:00:00 2001 From: ShuiRuTian <158983297@qq.com> Date: Thu, 9 Jul 2020 11:36:47 +0800 Subject: [PATCH 5/5] fix spelling. --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30bb33e52fd1a..c796cde271408 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28195,7 +28195,7 @@ 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. + // Take any/unknown as a special condition. Or maybe we could change `type` to a union containing all primitive types. if (type.flags & TypeFlags.AnyOrUnknown) { return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; }