From 12d1ea50ca55bb724d3cef40ecd88f8dbacfd3e1 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Fri, 8 Sep 2017 10:50:05 -0700 Subject: [PATCH] Merge pull request #18165 from amcasey/GH18144 Simplify and correct PermittedJumps computation (cherry picked from commit deefb01c9d53020a46a1692bf73b34696dad5c0c) --- src/harness/unittests/extractMethods.ts | 33 ++++++++++ src/services/refactors/extractMethod.ts | 66 ++++++++----------- .../extractMethod/extractMethod22.ts | 31 +++++++++ 3 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 tests/baselines/reference/extractMethod/extractMethod22.ts diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts index 72617bf7740..b6a3ee7cfe1 100644 --- a/src/harness/unittests/extractMethods.ts +++ b/src/harness/unittests/extractMethods.ts @@ -378,6 +378,30 @@ namespace A { "Cannot extract range containing conditional return statement." ]); + testExtractRangeFailed("extractRangeFailed7", + ` +function test(x: number) { + while (x) { + x--; + [#|break;|] + } +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + testExtractRangeFailed("extractRangeFailed8", + ` +function test(x: number) { + switch (x) { + case 1: + [#|break;|] + } +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single token."]); testExtractMethod("extractMethod1", @@ -561,6 +585,15 @@ namespace A { let x = 10; [#|x++; return;|] +}`); + // Return in finally block + testExtractMethod("extractMethod22", + `function test() { + try { + } + finally { + [#|return 1;|] + } }`); }); diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractMethod.ts index bb9df097940..4849b75c348 100644 --- a/src/services/refactors/extractMethod.ts +++ b/src/services/refactors/extractMethod.ts @@ -340,45 +340,31 @@ namespace ts.refactor.extractMethod { return false; } const savedPermittedJumps = permittedJumps; - if (node.parent) { - switch (node.parent.kind) { - case SyntaxKind.IfStatement: - if ((node.parent).thenStatement === node || (node.parent).elseStatement === node) { - // forbid all jumps inside thenStatement or elseStatement - permittedJumps = PermittedJumps.None; - } - break; - case SyntaxKind.TryStatement: - if ((node.parent).tryBlock === node) { - // forbid all jumps inside try blocks - permittedJumps = PermittedJumps.None; - } - else if ((node.parent).finallyBlock === node) { - // allow unconditional returns from finally blocks - permittedJumps = PermittedJumps.Return; - } - break; - case SyntaxKind.CatchClause: - if ((node.parent).block === node) { - // forbid all jumps inside the block of catch clause - permittedJumps = PermittedJumps.None; - } - break; - case SyntaxKind.CaseClause: - if ((node).expression !== node) { - // allow unlabeled break inside case clauses - permittedJumps |= PermittedJumps.Break; - } - break; - default: - if (isIterationStatement(node.parent, /*lookInLabeledStatements*/ false)) { - if ((node.parent).statement === node) { - // allow unlabeled break/continue inside loops - permittedJumps |= PermittedJumps.Break | PermittedJumps.Continue; - } - } - break; - } + + switch (node.kind) { + case SyntaxKind.IfStatement: + permittedJumps = PermittedJumps.None; + break; + case SyntaxKind.TryStatement: + // forbid all jumps inside try blocks + permittedJumps = PermittedJumps.None; + break; + case SyntaxKind.Block: + if (node.parent && node.parent.kind === SyntaxKind.TryStatement && (node).finallyBlock === node) { + // allow unconditional returns from finally blocks + permittedJumps = PermittedJumps.Return; + } + break; + case SyntaxKind.CaseClause: + // allow unlabeled break inside case clauses + permittedJumps |= PermittedJumps.Break; + break; + default: + if (isIterationStatement(node, /*lookInLabeledStatements*/ false)) { + // allow unlabeled break/continue inside loops + permittedJumps |= PermittedJumps.Break | PermittedJumps.Continue; + } + break; } switch (node.kind) { @@ -405,7 +391,7 @@ namespace ts.refactor.extractMethod { } } else { - if (!(permittedJumps & (SyntaxKind.BreakStatement ? PermittedJumps.Break : PermittedJumps.Continue))) { + if (!(permittedJumps & (node.kind === SyntaxKind.BreakStatement ? PermittedJumps.Break : PermittedJumps.Continue))) { // attempt to break or continue in a forbidden context (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements)); } diff --git a/tests/baselines/reference/extractMethod/extractMethod22.ts b/tests/baselines/reference/extractMethod/extractMethod22.ts new file mode 100644 index 00000000000..9b85b89559d --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod22.ts @@ -0,0 +1,31 @@ +// ==ORIGINAL== +function test() { + try { + } + finally { + return 1; + } +} +// ==SCOPE::function 'test'== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } + + function newFunction() { + return 1; + } +} +// ==SCOPE::global scope== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } +} +function newFunction() { + return 1; +}