Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KT-19762 Add inspection for unclosed (Auto)Closeable with quickfix to wrap usage in use #2082

Closed
wants to merge 1 commit into from

Conversation

DeFuex
Copy link
Contributor

@DeFuex DeFuex commented Jan 27, 2019

Copy link
Contributor

@goodwinnk goodwinnk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this amazing contribution. I see that a lot of work has been done but unfortunately a lot of corner cases are left. This is a hard inspection to implement and probably it should not have been marked with Up For Grabs tag.

I have commented the original request at https://youtrack.jetbrains.com/issue/KT-19762 and think that we need to discuss the corner cases there before putting additional efforts to the implementation.

If you're going to continue working on this inspection I suggest to wait for discussion end and divide the task to sub tasks. For example you could start from inspection only for some particular scenario. After that's is done and approved we could gradually add quick fixes. Also it probably should be two different inspections for the code that has an explicit close() and for code that doesn't.

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.ConvertResourceToUseCallIntention
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is outdated. Please run Generate All Tests shared configuration, and re-run tests. LocalInspectionTestGenerated.java file is expected to be modified after this launch, and this change should also be commited.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some additional tests. Please note, that there's no need to fix them all, some fixes may be postponed if the fix is hard. I think the one with exception is most important. A test with try/finally also should be added to the test suite. I don't like the one with false positive too.

import java.io.BufferedReader
import java.io.File

fun test1(args: Array<String>, reader2: BufferedReader) {
    val reader = File("hello-world.txt").bufferedReader()
    if (args.size > 2) {
        try {
            reader.readLine()
        } finally {
            reader.close()
        }

        return
    }

    reader.readLine() // Exception on fix apply
    reader.close()
}

fun test2(reader: BufferedReader) {
    reader.readLine() // No warnings
    reader.close()
}

fun test3(reader: BufferedReader) {
    try {
        reader.readLine()
    } finally {
        // Comment that is deleted after fix
        reader.close()
    }
}

fun test4(reader: BufferedReader) {
    // Works well, but should be added as an additional test.
    try {
        reader.readLine()
    } finally {
        reader.close()
    }
}

fun test5(reader: BufferedReader): BufferedReader {
    val reader = File("hello-world.txt").bufferedReader()
    reader.toString() // False positive
    return reader
}


fun main(args: Array<String>) {
File("hello-world.txt").bufferedReader().use { reader ->
reader.close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this a valid fix in general case. This is not a refactoring and what if close() throws an exception on the second execution?


var variableAssigned = false
// Differentiate between resource of variable or non variable reference expression
expression.parent.accept(object : KtTreeVisitorVoid() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is visitor needed? Can only parent be checked?

var dotParent: Boolean = false

// Check if variable assigned, non-variable with nested DotQualifiedExpression or just non-variable
when {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code should be rewritten. It's really difficult to understand what's going on.

}

// QuickFix for non variable assigned method call.
class ChangeResourceWithUseCall(private val dotParent: Boolean, private val pointer: SmartPsiElementPointer<KtDotQualifiedExpression>) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add Fix suffix and make those classes private. I think selecting better names for classes could make preceding comments redundant.

dotParent -> {
dotCallExpression = pointer.element as KtDotQualifiedExpression
methodResourceCallExpression =
(pointer.element!!.parent as KtDotQualifiedExpression).selectorExpression as KtCallExpression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use !! that much. From our practice, many such calls were fixed after getting an unhanded exception. I think pointer.element is a good candidate for extracting to local variable.

@DeFuex
Copy link
Contributor Author

DeFuex commented Jan 30, 2019

Thank you for this amazing contribution. I see that a lot of work has been done but unfortunately a lot of corner cases are left. This is a hard inspection to implement and probably it should not have been marked with Up For Grabs tag.

I have commented the original request at https://youtrack.jetbrains.com/issue/KT-19762 and think that we need to discuss the corner cases there before putting additional efforts to the implementation.

If you're going to continue working on this inspection I suggest to wait for discussion end and divide the task to sub tasks. For example you could start from inspection only for some particular scenario. After that's is done and approved we could gradually add quick fixes. Also it probably should be two different inspections for the code that has an explicit close() and for code that doesn't.

I understand that it needs to be more defined and wasn't expecting it to be in the previous Kotlin release 😄 ...i think it just would be a waste of effort if i would stop now. Just let me know when you guys think that things are ready. In the meanwhile i'll try to improve the code and add more test cases when i got time.

In any case, currently it should be testable from a perspective that it inspects every function call from an instance, assigned to variables (also non-assigned) and when it is derived from a Closeable. And of course the convertion works too...a lot of rough testing went into this... but i see what you mean with instances that shouldn't be leaked and close(), referring to your youtrack comment.

@goodwinnk goodwinnk added the Inspections Intentions and Inspections label May 17, 2019
@goodwinnk
Copy link
Contributor

We now have more strict set of rules for adding new inspections and intentions and trying to avoid adding new checks when they have a big chance of false positives.

Nobody is driving this inspection right now, so I'm going to close this pull-request. There is a link left in the ticket to this PR so your work could be reused when working on the (Auto)Closeable inspection is continued.

Thank you for work and time and I'm sorry that we didn't recognize potential problems with this inspection beforehand.

@goodwinnk goodwinnk closed this Jul 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Inspections Intentions and Inspections
Projects
None yet
3 participants