Suspend call detector: fix delegated properties checking

This commit is contained in:
Mikhail Glukhikh
2017-12-07 11:46:04 +03:00
parent 24bd31457c
commit e503c1d411
9 changed files with 102 additions and 4 deletions
@@ -0,0 +1,8 @@
fun bar(d: Delegate): String {
val x: String by <!ILLEGAL_SUSPEND_FUNCTION_CALL!>d<!>
return x
}
class Delegate {
suspend operator fun getValue(thisRef: Any?, property: Any?): String = ""
}
@@ -0,0 +1,11 @@
package
public fun bar(/*0*/ d: Delegate): kotlin.String
public final class Delegate {
public constructor Delegate()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final operator suspend fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.Any?): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1295,6 +1295,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("illegalSuspendCallsForDelegated.kt")
public void testIllegalSuspendCallsForDelegated() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.kt");
doTest(fileName);
}
@TestMetadata("irrelevantSuspendDeclarations.kt")
public void testIrrelevantSuspendDeclarations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/irrelevantSuspendDeclarations.kt");
@@ -1295,6 +1295,12 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
doTest(fileName);
}
@TestMetadata("illegalSuspendCallsForDelegated.kt")
public void testIllegalSuspendCallsForDelegated() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.kt");
doTest(fileName);
}
@TestMetadata("irrelevantSuspendDeclarations.kt")
public void testIrrelevantSuspendDeclarations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/irrelevantSuspendDeclarations.kt");
@@ -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)