Suspend call detector: fix delegated properties checking
This commit is contained in:
+20
-4
@@ -24,11 +24,14 @@ import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.descriptors.accessors
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.refactoring.getLineNumber
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
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.lazy.BodyResolveMode
|
||||
|
||||
@@ -76,7 +79,7 @@ class KotlinSuspendCallLineMarkerProvider : LineMarkerProvider {
|
||||
}
|
||||
|
||||
private fun KtExpression.isValidCandidateExpression(): Boolean {
|
||||
if (this is KtOperationReferenceExpression || this is KtForExpression) return true
|
||||
if (this is KtOperationReferenceExpression || this is KtForExpression || this is KtProperty) return true
|
||||
val parent = parent
|
||||
if (parent is KtCallExpression && parent.calleeExpression == this) return true
|
||||
return false
|
||||
@@ -87,13 +90,26 @@ fun KtExpression.hasSuspendCalls(bindingContext: BindingContext = analyze(BodyRe
|
||||
|
||||
return when (this) {
|
||||
is KtForExpression -> {
|
||||
val iteratorResolvedCall = bindingContext[BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange]
|
||||
val loopRangeHasNextResolvedCall = bindingContext[BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, loopRange]
|
||||
val loopRangeNextResolvedCall = bindingContext[BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange]
|
||||
val iteratorResolvedCall = bindingContext[LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange]
|
||||
val loopRangeHasNextResolvedCall = bindingContext[LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, loopRange]
|
||||
val loopRangeNextResolvedCall = bindingContext[LOOP_RANGE_NEXT_RESOLVED_CALL, loopRange]
|
||||
listOf(iteratorResolvedCall, loopRangeHasNextResolvedCall, loopRangeNextResolvedCall).any {
|
||||
it?.resultingDescriptor?.isSuspend == true
|
||||
}
|
||||
}
|
||||
is KtProperty -> {
|
||||
if (hasDelegateExpression()) {
|
||||
val variableDescriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, this] as? VariableDescriptorWithAccessors
|
||||
val accessors = variableDescriptor?.accessors ?: emptyList()
|
||||
accessors.any { accessor ->
|
||||
val delegatedFunctionDescriptor = bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, accessor]?.resultingDescriptor
|
||||
delegatedFunctionDescriptor?.isSuspend == true
|
||||
}
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val resolvedCall = getResolvedCall(bindingContext)
|
||||
(resolvedCall?.resultingDescriptor as? FunctionDescriptor)?.isSuspend == true
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.RedundantSuspendModifierInspection
|
||||
@@ -0,0 +1,13 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
<caret>suspend fun bar(): String {
|
||||
val x: String by Delegate()
|
||||
return x
|
||||
}
|
||||
|
||||
class Delegate {
|
||||
suspend operator fun getValue(thisRef: Any?, property: KProperty<*>): String = ""
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
<caret>suspend fun bar(): String {
|
||||
var x: String by Delegate()
|
||||
x = "Hello"
|
||||
return x
|
||||
}
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = ""
|
||||
|
||||
suspend operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {}
|
||||
}
|
||||
@@ -1977,6 +1977,27 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/redundantSuspend")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RedundantSuspend extends AbstractLocalInspectionTest {
|
||||
public void testAllFilesPresentInRedundantSuspend() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantSuspend"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("getterDelegate.kt")
|
||||
public void testGetterDelegate() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSuspend/getterDelegate.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("setterDelegate.kt")
|
||||
public void testSetterDelegate() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSuspend/setterDelegate.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/redundantUnitExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user