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
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.psi.*
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.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -33,7 +36,7 @@ class ConvertTryFinallyToUseCallInspection : IntentionBasedInspection<KtTryExpre
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()"
) {
override fun applyTo(element: KtTryExpression, editor: Editor?) {
@@ -69,36 +72,37 @@ class ConvertTryFinallyToUseCallIntention : SelfTargetingOffsetIndependentIntent
element.replace(useCallExpression)
}
override fun isApplicableTo(element: KtTryExpression): Boolean {
override fun applicabilityRange(element: KtTryExpression): TextRange? {
// Single statement in finally, no catch blocks
val finallySection = element.finallyBlock ?: return false
val finallyExpression = finallySection.finalExpression.statements.singleOrNull() ?: return false
if (element.catchClauses.isNotEmpty()) return false
val finallySection = element.finallyBlock ?: return null
val finallyExpression = finallySection.finalExpression.statements.singleOrNull() ?: return null
if (element.catchClauses.isNotEmpty()) return null
val context = element.analyze()
val resolvedCall = finallyExpression.getResolvedCall(context) ?: return false
if (resolvedCall.candidateDescriptor.name.asString() != "close") return false
if (resolvedCall.extensionReceiver != null) return false
val receiver = resolvedCall.dispatchReceiver ?: return false
val resolvedCall = finallyExpression.getResolvedCall(context) ?: return null
if (resolvedCall.candidateDescriptor.name.asString() != "close") return null
if (resolvedCall.extensionReceiver != null) return null
val receiver = resolvedCall.dispatchReceiver ?: return null
if (receiver.type.supertypes().all {
it.constructor.declarationDescriptor?.fqNameSafe?.asString().let {
it != "java.io.Closeable" && it != "java.lang.AutoCloseable"
}
}) return false
}) return null
return when (receiver) {
when (receiver) {
is ExpressionReceiver -> {
val expression = receiver.expression
if (expression is KtThisExpression) true
else {
val resourceReference = expression as? KtReferenceExpression ?: return false
if (expression !is KtThisExpression) {
val resourceReference = expression as? KtReferenceExpression ?: return null
val resourceDescriptor =
context[BindingContext.REFERENCE_TARGET, resourceReference] as? VariableDescriptor ?: return false
!resourceDescriptor.isVar
context[BindingContext.REFERENCE_TARGET, resourceReference] as? VariableDescriptor ?: return null
if (resourceDescriptor.isVar) return null
}
}
is ImplicitReceiver -> true
else -> false
is ImplicitReceiver -> {}
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>) {
val reader = File("hello-world.txt").bufferedReader()
try {
<caret>try {
reader.readLine()
}
catch (e: IOException) {
}
<caret>finally {
finally {
reader.close()
}
}
@@ -3,10 +3,10 @@ import java.io.File
fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader()
try {
<caret>try {
// do stuff with reader
}
<caret>finally {
finally {
reader.close()
}
}
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader
fun BufferedReader.foo() {
try {
try <caret>{
readLine()
}
<caret>finally {
finally {
close()
}
}
@@ -4,10 +4,10 @@ import java.io.File
fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader()
try {
<caret>try {
reader.readLine()
}
<caret>finally {
finally {
reader.readLine()
}
}
@@ -4,10 +4,10 @@ import java.io.File
fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader()
try {
try<caret> {
reader.readLine()
}
<caret>finally {
finally {
reader.readLine()
reader.close()
}
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader
fun foo(reader: BufferedReader) {
try {
try <caret>{
reader.readLine()
}
<caret>finally {
finally {
reader.close()
}
}
@@ -5,11 +5,11 @@ import java.io.BufferedReader
fun bar() {}
fun foo(reader: BufferedReader?) {
try {
<caret>try {
reader?.readLine()
bar()
}
<caret>finally {
finally {
reader?.close()
}
}
@@ -3,10 +3,10 @@ import java.io.File
fun main(args: Array<String>) {
val reader = File("hello-world.txt").bufferedReader()
try {
try <caret>{
reader.readLine()
}
<caret>finally {
finally {
reader.close()
}
}
@@ -3,10 +3,10 @@ import java.io.File
import java.io.BufferedReader
fun BufferedReader.foo() {
try {
<caret>try {
this.readLine()
}
<caret>finally {
finally {
this.close()
}
}
@@ -8,10 +8,10 @@ class MyCloseable : Closeable {
fun process(x: Int) = x
fun Int.foo() {
try {
<caret>try {
this@MyCloseable.process(this)
}
<caret>finally {
finally {
this@MyCloseable.close()
}
}
@@ -3,12 +3,12 @@ import java.io.File
fun main(args: Array<String>) {
val writer = File("hello-world.txt").bufferedWriter()
try {
try <caret>{
writer.write("123")
writer.newLine()
writer.write("456")
}
<caret>finally {
finally {
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");
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")