From 59b260835492e63b7b9ee63aab31368d50dc3834 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 22 Nov 2016 19:14:56 +0300 Subject: [PATCH] "Convert try / finally to .use { } call intention introduced" #KT-12100 Fixed --- .../after.kt.template | 6 ++ .../before.kt.template | 9 +++ .../description.html | 5 ++ idea/src/META-INF/plugin.xml | 5 ++ .../ConvertTryFinallyToUseCallIntention.kt | 76 +++++++++++++++++++ .../convertTryFinallyToUseCall/.intention | 1 + .../convertTryFinallyToUseCall/catch.kt | 17 +++++ .../convertTryFinallyToUseCall/example.kt | 12 +++ .../example.kt.after | 7 ++ .../convertTryFinallyToUseCall/notClose.kt | 13 ++++ .../notOnlyClose.kt | 14 ++++ .../convertTryFinallyToUseCall/simple.kt | 12 +++ .../simple.kt.after | 7 ++ .../convertTryFinallyToUseCall/triple.kt | 14 ++++ .../triple.kt.after | 11 +++ .../intentions/IntentionTestGenerated.java | 45 +++++++++++ 16 files changed, 254 insertions(+) create mode 100644 idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertTryFinallyToUseCallIntention.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/.intention create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/catch.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/example.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/example.kt.after create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/notClose.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/notOnlyClose.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/simple.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/simple.kt.after create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/triple.kt create mode 100644 idea/testData/intentions/convertTryFinallyToUseCall/triple.kt.after diff --git a/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/after.kt.template new file mode 100644 index 00000000000..dccbaa3ac42 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/after.kt.template @@ -0,0 +1,6 @@ +fun main(args: Array) { + val writer = File("hello-world.txt").bufferedReader() + writer.use { writer -> + // do stuff with writer + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/before.kt.template new file mode 100644 index 00000000000..cd4a88bd93d --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/before.kt.template @@ -0,0 +1,9 @@ +fun main(args: Array) { + val writer = File("hello-world.txt").bufferedReader() + try { + // do stuff with writer + } + finally { + writer.close() + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/description.html b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/description.html new file mode 100644 index 00000000000..55e19b9eb73 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFinallyToUseCallIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a try-finally block with resource.close() in finally into the resource.use() call + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 61a575364bb..6e9f7959332 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1446,6 +1446,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertTryFinallyToUseCallIntention + Kotlin + + ( + KtTryExpression::class.java, "Convert try-finally to .use()" +) { + override fun applyTo(element: KtTryExpression, editor: Editor?) { + val finallySection = element.finallyBlock!! + val finallyDotCall = finallySection.finalExpression.statements.singleOrNull() as KtDotQualifiedExpression + val resourceReference = finallyDotCall.receiverExpression as KtNameReferenceExpression + + val factory = KtPsiFactory(element) + + val useCallExpression = factory.buildExpression { + appendName(resourceReference.getReferencedNameAsName()) + appendFixedText(".use {") + + appendName(resourceReference.getReferencedNameAsName()) + appendFixedText("->") + + appendChildRange(element.tryBlock.contentRange()) + appendFixedText("}") + } + + element.replace(useCallExpression) + } + + override fun isApplicableTo(element: KtTryExpression): Boolean { + // Single statement in finally, no catch blocks + val finallySection = element.finallyBlock ?: return false + val finallyDotCall = finallySection.finalExpression.statements.singleOrNull() as? KtDotQualifiedExpression ?: return false + if (element.catchClauses.isNotEmpty()) return false + + // Like resource.close() + val resourceReference = finallyDotCall.receiverExpression as? KtNameReferenceExpression ?: return false + val resourceCall = finallyDotCall.selectorExpression as? KtCallExpression ?: return false + if (resourceCall.calleeExpression?.text != "close") return false + + // resource is Closeable immutable local variable + val resourceDescriptor = + element.analyze().get(BindingContext.REFERENCE_TARGET, resourceReference) as? VariableDescriptor ?: return false + return !resourceDescriptor.isVar && resourceDescriptor.type.supertypes().any { + it.constructor.declarationDescriptor?.fqNameSafe?.asString().let { + it == "java.io.Closeable" || it == "java.lang.AutoCloseable" + } + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/.intention b/idea/testData/intentions/convertTryFinallyToUseCall/.intention new file mode 100644 index 00000000000..866d262224f --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertTryFinallyToUseCallIntention diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/catch.kt b/idea/testData/intentions/convertTryFinallyToUseCall/catch.kt new file mode 100644 index 00000000000..1291bd4e5f8 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/catch.kt @@ -0,0 +1,17 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME +import java.io.File +import java.io.IOException + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + try { + reader.readLine() + } + catch (e: IOException) { + + } + finally { + reader.close() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/example.kt b/idea/testData/intentions/convertTryFinallyToUseCall/example.kt new file mode 100644 index 00000000000..1b51cd6fe81 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/example.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + try { + // do stuff with reader + } + finally { + reader.close() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/example.kt.after b/idea/testData/intentions/convertTryFinallyToUseCall/example.kt.after new file mode 100644 index 00000000000..9c8f7810260 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/example.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + reader.use { reader -> // do stuff with reader } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/notClose.kt b/idea/testData/intentions/convertTryFinallyToUseCall/notClose.kt new file mode 100644 index 00000000000..d364490ee37 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/notClose.kt @@ -0,0 +1,13 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + try { + reader.readLine() + } + finally { + reader.readLine() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/notOnlyClose.kt b/idea/testData/intentions/convertTryFinallyToUseCall/notOnlyClose.kt new file mode 100644 index 00000000000..0e394be0741 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/notOnlyClose.kt @@ -0,0 +1,14 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + try { + reader.readLine() + } + finally { + reader.readLine() + reader.close() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt b/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt new file mode 100644 index 00000000000..7285f1fd011 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + try { + reader.readLine() + } + finally { + reader.close() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt.after b/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt.after new file mode 100644 index 00000000000..8d61c32a3a1 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/simple.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val reader = File("hello-world.txt").bufferedReader() + reader.use { reader -> reader.readLine() } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt b/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt new file mode 100644 index 00000000000..2762562f952 --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val writer = File("hello-world.txt").bufferedWriter() + try { + writer.write("123") + writer.newLine() + writer.write("456") + } + finally { + writer.close() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt.after b/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt.after new file mode 100644 index 00000000000..521acd5b83e --- /dev/null +++ b/idea/testData/intentions/convertTryFinallyToUseCall/triple.kt.after @@ -0,0 +1,11 @@ +// WITH_RUNTIME +import java.io.File + +fun main(args: Array) { + val writer = File("hello-world.txt").bufferedWriter() + writer.use { writer -> + writer.write("123") + writer.newLine() + writer.write("456") + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 088a5645565..de504b0ee01 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -6088,6 +6088,51 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } + @TestMetadata("idea/testData/intentions/convertTryFinallyToUseCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertTryFinallyToUseCall extends AbstractIntentionTest { + public void testAllFilesPresentInConvertTryFinallyToUseCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertTryFinallyToUseCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("catch.kt") + public void testCatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/catch.kt"); + doTest(fileName); + } + + @TestMetadata("example.kt") + public void testExample() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/example.kt"); + doTest(fileName); + } + + @TestMetadata("notClose.kt") + public void testNotClose() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/notClose.kt"); + doTest(fileName); + } + + @TestMetadata("notOnlyClose.kt") + public void testNotOnlyClose() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/notOnlyClose.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/simple.kt"); + doTest(fileName); + } + + @TestMetadata("triple.kt") + public void testTriple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/triple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/copyConcatenatedStringToClipboard") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)