Extend "async result unused" to "Deferred result unused" #KT-15063 Fixed
This commit is contained in:
+4
-4
@@ -15,12 +15,12 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
abstract class AbstractResultUnusedChecker(
|
||||
private val expressionChecker: (KtExpression) -> Boolean,
|
||||
private val callChecker: (ResolvedCall<*>) -> Boolean
|
||||
private val expressionChecker: (KtExpression, AbstractResultUnusedChecker) -> Boolean,
|
||||
private val callChecker: (ResolvedCall<*>, AbstractResultUnusedChecker) -> Boolean
|
||||
) : AbstractKotlinInspection() {
|
||||
protected fun check(expression: KtExpression): Boolean {
|
||||
// Check whatever possible by PSI
|
||||
if (!expressionChecker(expression)) return false
|
||||
if (!expressionChecker(expression, this)) return false
|
||||
var parent: PsiElement? = expression.parent
|
||||
while (parent != null) {
|
||||
if (parent is KtBlockExpression || parent is KtFunction || parent is KtFile) break
|
||||
@@ -32,6 +32,6 @@ abstract class AbstractResultUnusedChecker(
|
||||
val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
|
||||
if (expression.isUsedAsExpression(context)) return false
|
||||
val resolvedCall = expression.getResolvedCall(context) ?: return false
|
||||
return callChecker(resolvedCall)
|
||||
return callChecker(resolvedCall, this)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports <b>async</b> call that is never used,
|
||||
so all actions inside <b>async</b> are never executed.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports calls with <b>kotlinx.coroutines.experimental.Deferred</b> result that is never used,
|
||||
so nobody will wait for execution of all related actions.
|
||||
</body>
|
||||
</html>
|
||||
@@ -2854,8 +2854,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
@@ -2854,8 +2854,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
@@ -2854,8 +2854,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
@@ -2855,8 +2855,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
@@ -2854,8 +2854,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
@@ -2854,8 +2854,8 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
||||
displayName="Result of 'async' is not used"
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||
displayName="Deferred result is never used"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections.coroutines
|
||||
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractResultUnusedChecker
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.callExpressionVisitor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
|
||||
class AsyncResultUnusedInspection : AbstractResultUnusedChecker(
|
||||
expressionChecker = fun(expression): Boolean =
|
||||
expression is KtCallExpression && expression.calleeExpression?.text == "async",
|
||||
callChecker = fun(resolvedCall): Boolean =
|
||||
resolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == "kotlinx.coroutines.experimental.async"
|
||||
) {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
|
||||
callExpressionVisitor(fun(expression) {
|
||||
if (!check(expression)) return
|
||||
holder.registerProblem(expression.calleeExpression ?: expression, "Result of 'async' is never used")
|
||||
})
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections.coroutines
|
||||
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractResultUnusedChecker
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.callExpressionVisitor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import javax.swing.JComponent
|
||||
|
||||
class DeferredResultUnusedInspection(@JvmField var standardOnly: Boolean = true) : AbstractResultUnusedChecker(
|
||||
expressionChecker = fun(expression, inspection): Boolean =
|
||||
inspection is DeferredResultUnusedInspection && expression is KtCallExpression &&
|
||||
(!inspection.standardOnly || expression.calleeExpression?.text in shortNames),
|
||||
callChecker = fun(resolvedCall, inspection): Boolean {
|
||||
if (inspection !is DeferredResultUnusedInspection) return false
|
||||
return if (inspection.standardOnly) {
|
||||
resolvedCall.resultingDescriptor.fqNameOrNull() in fqNames
|
||||
} else {
|
||||
val returnTypeClassifier = resolvedCall.resultingDescriptor.returnType?.constructor?.declarationDescriptor
|
||||
returnTypeClassifier?.importableFqName == deferred
|
||||
}
|
||||
}
|
||||
) {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
|
||||
callExpressionVisitor(fun(expression) {
|
||||
if (!check(expression)) return
|
||||
holder.registerProblem(expression.calleeExpression ?: expression, "Deferred result is never used")
|
||||
})
|
||||
|
||||
override fun createOptionsPanel(): JComponent? {
|
||||
val panel = MultipleCheckboxOptionsPanel(this)
|
||||
panel.addCheckbox("Reports only function calls from kotlinx.coroutines", "standardOnly")
|
||||
return panel
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val shortNames = setOf("async")
|
||||
|
||||
private val fqNames: Set<FqName> = shortNames.mapTo(mutableSetOf()) { FqName("kotlinx.coroutines.experimental.$it") }
|
||||
|
||||
private val deferred = FqName("kotlinx.coroutines.experimental.Deferred")
|
||||
}
|
||||
}
|
||||
+20
-2
@@ -5,7 +5,25 @@
|
||||
<module>light_idea_test_case</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/simple.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Result of 'async' is not used</problem_class>
|
||||
<description>Result of 'async' is never used</description>
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
|
||||
<description>Deferred result is never used</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>simple.kt</file>
|
||||
<line>49</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/simple.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
|
||||
<description>Deferred result is never used</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>simple.kt</file>
|
||||
<line>51</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/simple.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
|
||||
<description>Deferred result is never used</description>
|
||||
</problem>
|
||||
</problems>
|
||||
+1
-1
@@ -1 +1 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<inspection_tool class="DeferredResultUnused" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="standardOnly" value="false" />
|
||||
</inspection_tool>
|
||||
@@ -37,3 +37,18 @@ fun falsePositives() {
|
||||
val res = async { 13 }
|
||||
useIt(async { 7 })
|
||||
}
|
||||
|
||||
class User
|
||||
|
||||
interface DbHandler {
|
||||
fun getUser(id: Long): Deferred<User>
|
||||
fun doStuff(): Deferred<Unit>
|
||||
}
|
||||
|
||||
fun DbHandler.test() {
|
||||
getUser(42L)
|
||||
val user = getUser(42L).await()
|
||||
doStuff()
|
||||
doStuff().await()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user