KT-12100 convert try / finally to use: report intention only at try {
This commit is contained in:
+22
-18
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user