Suspend call detector: fix delegated properties checking
This commit is contained in:
Vendored
+8
@@ -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 = ""
|
||||
}
|
||||
Vendored
+11
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+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