From 89419511803e3c3e8cae0ccad4a9678d272b199e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 2 Aug 2016 12:41:12 -0700 Subject: [PATCH 1/2] Check return paths on getters --- src/compiler/checker.ts | 13 +++-- .../getterControlFlowStrictNull.errors.txt | 27 +++++++++++ .../reference/getterControlFlowStrictNull.js | 47 +++++++++++++++++++ .../compiler/getterControlFlowStrictNull.ts | 20 ++++++++ 4 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/getterControlFlowStrictNull.errors.txt create mode 100644 tests/baselines/reference/getterControlFlowStrictNull.js create mode 100644 tests/cases/compiler/getterControlFlowStrictNull.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a45b61660a1f..7d25e9287ad77 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13916,14 +13916,10 @@ namespace ts { checkDecorators(node); checkSignatureDeclaration(node); + // TODO (weswig): Consolidate the below into checkAllCodePathsInNonVoidFunctionReturnOrThrow if (node.kind === SyntaxKind.GetAccessor) { if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { - if (node.flags & NodeFlags.HasExplicitReturn) { - if (compilerOptions.noImplicitReturns) { - error(node.name, Diagnostics.Not_all_code_paths_return_a_value); - } - } - else { + if (!(node.flags & NodeFlags.HasExplicitReturn)) { error(node.name, Diagnostics.A_get_accessor_must_return_a_value); } } @@ -13953,7 +13949,10 @@ namespace ts { checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); } } - getTypeOfAccessors(getSymbolOfNode(node)); + const returnType = getTypeOfAccessors(getSymbolOfNode(node)); + if (node.kind === SyntaxKind.GetAccessor) { + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); + } } if (node.parent.kind !== SyntaxKind.ObjectLiteralExpression) { checkSourceElement(node.body); diff --git a/tests/baselines/reference/getterControlFlowStrictNull.errors.txt b/tests/baselines/reference/getterControlFlowStrictNull.errors.txt new file mode 100644 index 0000000000000..1c4f02d6a756f --- /dev/null +++ b/tests/baselines/reference/getterControlFlowStrictNull.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/getterControlFlowStrictNull.ts(2,9): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. +tests/cases/compiler/getterControlFlowStrictNull.ts(11,14): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + + +==== tests/cases/compiler/getterControlFlowStrictNull.ts (2 errors) ==== + class A { + a(): string | null { + ~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } + } + class B { + get a(): string | null { + ~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } + } \ No newline at end of file diff --git a/tests/baselines/reference/getterControlFlowStrictNull.js b/tests/baselines/reference/getterControlFlowStrictNull.js new file mode 100644 index 0000000000000..c3f7e410d0a7c --- /dev/null +++ b/tests/baselines/reference/getterControlFlowStrictNull.js @@ -0,0 +1,47 @@ +//// [getterControlFlowStrictNull.ts] +class A { + a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } +} +class B { + get a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } +} + +//// [getterControlFlowStrictNull.js] +var A = (function () { + function A() { + } + A.prototype.a = function () { + if (Math.random() > 0.5) { + return ''; + } + // it does error here as expected + }; + return A; +}()); +var B = (function () { + function B() { + } + Object.defineProperty(B.prototype, "a", { + get: function () { + if (Math.random() > 0.5) { + return ''; + } + // it should error here because it returns undefined + }, + enumerable: true, + configurable: true + }); + return B; +}()); diff --git a/tests/cases/compiler/getterControlFlowStrictNull.ts b/tests/cases/compiler/getterControlFlowStrictNull.ts new file mode 100644 index 0000000000000..44fbe35978942 --- /dev/null +++ b/tests/cases/compiler/getterControlFlowStrictNull.ts @@ -0,0 +1,20 @@ +//@strictNullChecks: true +//@target: ES5 +class A { + a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it does error here as expected + } +} +class B { + get a(): string | null { + if (Math.random() > 0.5) { + return ''; + } + + // it should error here because it returns undefined + } +} \ No newline at end of file From 720cf54547b848e1d75501095dd1f61f2cdbf41b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 17 Aug 2016 11:47:55 -0700 Subject: [PATCH 2/2] Remove TODO comment --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7d25e9287ad77..0cc0ebab32099 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13916,7 +13916,6 @@ namespace ts { checkDecorators(node); checkSignatureDeclaration(node); - // TODO (weswig): Consolidate the below into checkAllCodePathsInNonVoidFunctionReturnOrThrow if (node.kind === SyntaxKind.GetAccessor) { if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { if (!(node.flags & NodeFlags.HasExplicitReturn)) {