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
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
abstract class AbstractResultUnusedChecker(
|
abstract class AbstractResultUnusedChecker(
|
||||||
private val expressionChecker: (KtExpression) -> Boolean,
|
private val expressionChecker: (KtExpression, AbstractResultUnusedChecker) -> Boolean,
|
||||||
private val callChecker: (ResolvedCall<*>) -> Boolean
|
private val callChecker: (ResolvedCall<*>, AbstractResultUnusedChecker) -> Boolean
|
||||||
) : AbstractKotlinInspection() {
|
) : AbstractKotlinInspection() {
|
||||||
protected fun check(expression: KtExpression): Boolean {
|
protected fun check(expression: KtExpression): Boolean {
|
||||||
// Check whatever possible by PSI
|
// Check whatever possible by PSI
|
||||||
if (!expressionChecker(expression)) return false
|
if (!expressionChecker(expression, this)) return false
|
||||||
var parent: PsiElement? = expression.parent
|
var parent: PsiElement? = expression.parent
|
||||||
while (parent != null) {
|
while (parent != null) {
|
||||||
if (parent is KtBlockExpression || parent is KtFunction || parent is KtFile) break
|
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)
|
val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
|
||||||
if (expression.isUsedAsExpression(context)) return false
|
if (expression.isUsedAsExpression(context)) return false
|
||||||
val resolvedCall = expression.getResolvedCall(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"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
@@ -2854,8 +2854,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
@@ -2854,8 +2854,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
@@ -2855,8 +2855,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
@@ -2854,8 +2854,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
@@ -2854,8 +2854,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredResultUnusedInspection"
|
||||||
displayName="Result of 'async' is not used"
|
displayName="Deferred result is never used"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Probable bugs"
|
groupName="Probable bugs"
|
||||||
enabledByDefault="true"
|
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>
|
<module>light_idea_test_case</module>
|
||||||
<package><default></package>
|
<package><default></package>
|
||||||
<entry_point TYPE="file" FQNAME="temp:///src/simple.kt" />
|
<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>
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
|
||||||
<description>Result of 'async' is never used</description>
|
<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>
|
</problem>
|
||||||
</problems>
|
</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 }
|
val res = async { 13 }
|
||||||
useIt(async { 7 })
|
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