KT-12100 convert try / finally to use: report intention only at try {

This commit is contained in:
Mikhail Glukhikh
2016-12-01 19:36:53 +03:00
parent 0cc52e2ff8
commit 9ea326870d
14 changed files with 63 additions and 40 deletions
@@ -17,11 +17,14 @@
package org.jetbrains.kotlin.idea.intentions package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contentRange import org.jetbrains.kotlin.psi.psiUtil.contentRange
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -33,7 +36,7 @@ class ConvertTryFinallyToUseCallInspection : IntentionBasedInspection<KtTryExpre
override fun inspectionTarget(element: KtTryExpression) = element.tryKeyword ?: element.tryBlock override fun inspectionTarget(element: KtTryExpression) = element.tryKeyword ?: element.tryBlock
} }
class ConvertTryFinallyToUseCallIntention : SelfTargetingOffsetIndependentIntention<KtTryExpression>( class ConvertTryFinallyToUseCallIntention : SelfTargetingRangeIntention<KtTryExpression>(
KtTryExpression::class.java, "Convert try-finally to .use()" KtTryExpression::class.java, "Convert try-finally to .use()"
) { ) {
override fun applyTo(element: KtTryExpression, editor: Editor?) { override fun applyTo(element: KtTryExpression, editor: Editor?) {
@@ -69,36 +72,37 @@ class ConvertTryFinallyToUseCallIntention : SelfTargetingOffsetIndependentIntent
element.replace(useCallExpression) element.replace(useCallExpression)
} }
override fun isApplicableTo(element: KtTryExpression): Boolean { override fun applicabilityRange(element: KtTryExpression): TextRange? {
// Single statement in finally, no catch blocks // Single statement in finally, no catch blocks
val finallySection = element.finallyBlock ?: return false val finallySection = element.finallyBlock ?: return null
val finallyExpression = finallySection.finalExpression.statements.singleOrNull() ?: return false val finallyExpression = finallySection.finalExpression.statements.singleOrNull() ?: return null
if (element.catchClauses.isNotEmpty()) return false if (element.catchClauses.isNotEmpty()) return null
val context = element.analyze() val context = element.analyze()
val resolvedCall = finallyExpression.getResolvedCall(context) ?: return false val resolvedCall = finallyExpression.getResolvedCall(context) ?: return null
if (resolvedCall.candidateDescriptor.name.asString() != "close") return false if (resolvedCall.candidateDescriptor.name.asString() != "close") return null
if (resolvedCall.extensionReceiver != null) return false if (resolvedCall.extensionReceiver != null) return null
val receiver = resolvedCall.dispatchReceiver ?: return false val receiver = resolvedCall.dispatchReceiver ?: return null
if (receiver.type.supertypes().all { if (receiver.type.supertypes().all {
it.constructor.declarationDescriptor?.fqNameSafe?.asString().let { it.constructor.declarationDescriptor?.fqNameSafe?.asString().let {
it != "java.io.Closeable" && it != "java.lang.AutoCloseable" it != "java.io.Closeable" && it != "java.lang.AutoCloseable"
} }
}) return false }) return null
return when (receiver) { when (receiver) {
is ExpressionReceiver -> { is ExpressionReceiver -> {
val expression = receiver.expression val expression = receiver.expression
if (expression is KtThisExpression) true if (expression !is KtThisExpression) {
else { val resourceReference = expression as? KtReferenceExpression ?: return null
val resourceReference = expression as? KtReferenceExpression ?: return false
val resourceDescriptor = val resourceDescriptor =
context[BindingContext.REFERENCE_TARGET, resourceReference] as? VariableDescriptor ?: return false context[BindingContext.REFERENCE_TARGET, resourceReference] as? VariableDescriptor ?: return null
!resourceDescriptor.isVar if (resourceDescriptor.isVar) return null
} }
} }
is ImplicitReceiver -> true is ImplicitReceiver -> {}
else -> false else -> return null
} }
return TextRange(element.startOffset, element.tryBlock.lBrace?.endOffset ?: element.endOffset)
} }
} }
@@ -5,13 +5,13 @@ import java.io.IOException
fun main(args: Array<String>) { fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader() val reader = File("hello-world.txt").bufferedReader()
try { <caret>try {
reader.readLine() reader.readLine()
} }
catch (e: IOException) { catch (e: IOException) {
} }
<caret>finally { finally {
reader.close() reader.close()
} }
} }
@@ -3,10 +3,10 @@ import java.io.File
fun main(args: Array<String>) { fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader() val reader = File("hello-world.txt").bufferedReader()
try { <caret>try {
// do stuff with reader // do stuff with reader
} }
<caret>finally { finally {
reader.close() reader.close()
} }
} }
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader import java.io.BufferedReader
fun BufferedReader.foo() { fun BufferedReader.foo() {
try { try <caret>{
readLine() readLine()
} }
<caret>finally { finally {
close() close()
} }
} }
@@ -4,10 +4,10 @@ import java.io.File
fun main(args: Array<String>) { fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader() val reader = File("hello-world.txt").bufferedReader()
try { <caret>try {
reader.readLine() reader.readLine()
} }
<caret>finally { finally {
reader.readLine() reader.readLine()
} }
} }
@@ -4,10 +4,10 @@ import java.io.File
fun main(args: Array<String>) { fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader() val reader = File("hello-world.txt").bufferedReader()
try { try<caret> {
reader.readLine() reader.readLine()
} }
<caret>finally { finally {
reader.readLine() reader.readLine()
reader.close() reader.close()
} }
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader import java.io.BufferedReader
fun foo(reader: BufferedReader) { fun foo(reader: BufferedReader) {
try { try <caret>{
reader.readLine() reader.readLine()
} }
<caret>finally { finally {
reader.close() reader.close()
} }
} }
@@ -5,11 +5,11 @@ import java.io.BufferedReader
fun bar() {} fun bar() {}
fun foo(reader: BufferedReader?) { fun foo(reader: BufferedReader?) {
try { <caret>try {
reader?.readLine() reader?.readLine()
bar() bar()
} }
<caret>finally { finally {
reader?.close() reader?.close()
} }
} }
@@ -3,10 +3,10 @@ import java.io.File
fun main(args: Array<String>) { fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader() val reader = File("hello-world.txt").bufferedReader()
try { try <caret>{
reader.readLine() reader.readLine()
} }
<caret>finally { finally {
reader.close() reader.close()
} }
} }
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader import java.io.BufferedReader
fun BufferedReader.foo() { fun BufferedReader.foo() {
try { <caret>try {
this.readLine() this.readLine()
} }
<caret>finally { finally {
this.close() this.close()
} }
} }
@@ -8,10 +8,10 @@ class MyCloseable : Closeable {
fun process(x: Int) = x fun process(x: Int) = x
fun Int.foo() { fun Int.foo() {
try { <caret>try {
this@MyCloseable.process(this) this@MyCloseable.process(this)
} }
<caret>finally { finally {
this@MyCloseable.close() this@MyCloseable.close()
} }
} }
@@ -3,12 +3,12 @@ import java.io.File
fun main(args: Array<String>) { fun main(args: Array<String>) {
val writer = File("hello-world.txt").bufferedWriter() val writer = File("hello-world.txt").bufferedWriter()
try { try <caret>{
writer.write("123") writer.write("123")
writer.newLine() writer.newLine()
writer.write("456") writer.write("456")
} }
<caret>finally { finally {
writer.close() writer.close()
} }
} }
@@ -0,0 +1,13 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
import java.io.File
fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader()
try {
reader.readLine()
}
finally {
<caret>reader.close()
}
}
@@ -6161,6 +6161,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/triple.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/triple.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("wrongCaret.kt")
public void testWrongCaret() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertTryFinallyToUseCall/wrongCaret.kt");
doTest(fileName);
}
} }
@TestMetadata("idea/testData/intentions/copyConcatenatedStringToClipboard") @TestMetadata("idea/testData/intentions/copyConcatenatedStringToClipboard")