From d3c14ce7d61670c202dee48a188121c6e5957b15 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 5 Oct 2016 14:17:29 +0300 Subject: [PATCH] Extract Function: Support implicit abnormal exits via Nothing-typed expressions --- ChangeLog.md | 1 + .../org/jetbrains/kotlin/cfg/pseudocode/Pseudocode.kt | 2 ++ .../jetbrains/kotlin/cfg/pseudocode/PseudocodeImpl.kt | 2 +- .../kotlin/cfg/pseudocode/pseudocodeUtils.kt | 7 ++++++- .../extractionEngine/extractableAnalysisUtil.kt | 4 +++- .../controlFlow/throws/implicitThrow.kt | 7 +++++++ .../controlFlow/throws/implicitThrow.kt.after | 11 +++++++++++ .../introduce/ExtractionTestGenerated.java | 6 ++++++ 8 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt create mode 100644 idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt.after diff --git a/ChangeLog.md b/ChangeLog.md index 41a5e3a329b..b35c36609a9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -242,6 +242,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-14128`](https://youtrack.jetbrains.com/issue/KT-14128), [`KT-13862`](https://youtrack.jetbrains.com/issue/KT-13862) Rename: Use qualified class name when looking for occurrences in non-code files - [`KT-6199`](https://youtrack.jetbrains.com/issue/KT-6199) Rename: Replace non-code class occurrences with new qualified name - [`KT-14182`](https://youtrack.jetbrains.com/issue/KT-14182) Move: Show error message on applying to enum entries +- Extract Function: Support implicit abnormal exits via Nothing-typed expressions ##### New features diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/Pseudocode.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/Pseudocode.kt index 746beea90b1..b387e2360d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/Pseudocode.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/Pseudocode.kt @@ -39,6 +39,8 @@ interface Pseudocode { val exitInstruction: SubroutineExitInstruction + val errorInstruction: SubroutineExitInstruction + val sinkInstruction: SubroutineSinkInstruction val enterInstruction: SubroutineEnterInstruction diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeImpl.kt index bbfea60527c..303eb1eaf1c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeImpl.kt @@ -71,7 +71,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode private var internalErrorInstruction: SubroutineExitInstruction? = null - private val errorInstruction: SubroutineExitInstruction + override val errorInstruction: SubroutineExitInstruction get() = internalErrorInstruction ?: throw AssertionError("Error instruction is read before initialization") private var postPrecessed = false diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt index 1f3e38f5c1f..db890805a9b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt @@ -18,9 +18,11 @@ package org.jetbrains.kotlin.cfg.pseudocode import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.cfg.Label import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind.* +import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInstruction @@ -307,4 +309,7 @@ fun Pseudocode.getPseudocodeByElement(element: KtElement): Pseudocode? { localDeclarations.forEach { decl -> decl.body.getPseudocodeByElement(element)?.let { return it } } return null -} \ No newline at end of file +} + +val Label.isJumpToError: Boolean + get() = resolveToInstruction() == pseudocode.errorInstruction \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 59714a55fb6..d3aa9950998 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -243,6 +243,8 @@ private fun ExtractionData.analyzeControlFlow( when { it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode -> null + it is UnconditionalJumpInstruction && it.targetLabel.isJumpToError -> + it e != null && e !is KtBreakExpression && e !is KtContinueExpression -> it.previousInstructions.firstOrNull() else -> @@ -268,7 +270,7 @@ private fun ExtractionData.analyzeControlFlow( || element is KtContinueExpression) { jumpExits.add(inst) } - else if (element !is KtThrowExpression) { + else if (element !is KtThrowExpression && !inst.targetLabel.isJumpToError) { defaultExits.add(inst) } } diff --git a/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt b/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt new file mode 100644 index 00000000000..a52aabb5a77 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// PARAM_DESCRIPTOR: value-parameter a: kotlin.Int? defined in foo +// PARAM_TYPES: kotlin.Int? +fun foo(a: Int?): Int { + val n = a ?: error("") + return n + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt.after b/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt.after new file mode 100644 index 00000000000..f6cc7bcc610 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt.after @@ -0,0 +1,11 @@ +// WITH_RUNTIME +// PARAM_DESCRIPTOR: value-parameter a: kotlin.Int? defined in foo +// PARAM_TYPES: kotlin.Int? +fun foo(a: Int?): Int { + return i(a) +} + +private fun i(a: Int?): Int { + val n = a ?: error("") + return n + 1 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 092f25de0c0..89f1ea49c13 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -1682,6 +1682,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("implicitThrow.kt") + public void testImplicitThrow() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/throws/implicitThrow.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("nonValuedReturnWithThrow.kt") public void testNonValuedReturnWithThrow() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/throws/nonValuedReturnWithThrow.kt");