Fix "redundant async" for coroutines 1.*, forbid explicit scopes case

Related to KT-28504
#KT-28445 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-11-28 14:26:46 +03:00
parent 93fff99d8d
commit 05b1a99022
12 changed files with 65 additions and 35 deletions
@@ -9,6 +9,7 @@ import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import org.jetbrains.kotlin.idea.inspections.collections.AbstractCallChainChecker
import org.jetbrains.kotlin.idea.inspections.collections.SimplifyCallChainFix
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi.qualifiedExpressionVisitor
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
@@ -18,21 +19,21 @@ class RedundantAsyncInspection : AbstractCallChainChecker() {
qualifiedExpressionVisitor(fun(expression) {
var defaultContext: Boolean? = null
var defaultStart: Boolean? = null
var defaultParent: Boolean? = null
// Temporary forbid cases with explicit scope
if (expression.receiverExpression is KtQualifiedExpression) return
val conversion = findQualifiedConversion(expression, conversionGroups) check@{ _, firstResolvedCall, _, _ ->
for ((parameterDescriptor, valueArgument) in firstResolvedCall.valueArguments) {
val default = valueArgument is DefaultValueArgument
when (parameterDescriptor.name.asString()) {
"context" -> defaultContext = default
"start" -> defaultStart = default
"parent" -> defaultParent = default
}
}
true
} ?: return
defaultContext ?: return
defaultStart ?: return
if (defaultParent != true) return
if (defaultContext!! && !defaultStart!!) return
var replacement = conversion.replacement
@@ -71,7 +72,7 @@ class RedundantAsyncInspection : AbstractCallChainChecker() {
)
)
private const val defaultAsyncArgument = "$COROUTINE_PACKAGE.DefaultDispatcher"
private const val defaultAsyncArgument = "$COROUTINE_PACKAGE.Dispatchers.Default"
private const val defaultAsyncArgumentExperimental = "$COROUTINE_EXPERIMENTAL_PACKAGE.DefaultDispatcher"
}
@@ -6,7 +6,9 @@ interface Deferred<T> {
interface CoroutineContext
object DefaultDispatcher : CoroutineContext
object Dispatchers {
object Default : CoroutineContext
}
enum class CoroutineStart {
DEFAULT,
@@ -15,30 +17,28 @@ enum class CoroutineStart {
UNDISPATCHED
}
interface Job
fun <T> async(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
f: suspend () -> T
): Deferred<T> {
TODO()
interface CoroutineScope {
val coroutineContext: CoroutineContext get() = Dispatchers.Default
}
fun <T> runBlocking(
context: CoroutineContext = DefaultDispatcher,
f: suspend () -> T
) {
object GlobalScope : CoroutineScope
fun <T> CoroutineScope.async(
context: CoroutineContext = Dispatchers.Default,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
TODO()
}
suspend fun <T> withContext(
context: CoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
f: suspend () -> T
block: suspend CoroutineScope.() -> T
) {
TODO()
}
suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R = GlobalScope.block()
@@ -0,0 +1,9 @@
// WITH_RUNTIME
// PROBLEM: none
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext, scope: CoroutineScope) {
// Does not work yet (1.3.11)
scope.<caret>async(ctx) { 42 }.await()
}
@@ -1,8 +1,9 @@
// PROBLEM: none
// WITH_RUNTIME
// PROBLEM: none
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
<caret>async(parent = null) { 42 }.await()
// Does not work yet (1.3.11)
GlobalScope.<caret>async(ctx) { 42 }.await()
}
@@ -4,5 +4,7 @@
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
<caret>async(ctx) { 42 }.await()
coroutineScope {
<caret>async(ctx) { 42 }.await()
}
}
@@ -4,5 +4,7 @@
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
withContext(ctx) { 42 }
coroutineScope {
withContext(ctx) { 42 }
}
}
@@ -1,8 +1,10 @@
// WITH_RUNTIME
// FIX: Merge call chain to 'withContext(DefaultDispatcher)'
// FIX: Merge call chain to 'withContext(Default)'
package kotlinx.coroutines
suspend fun test() {
<caret>async { 42 }.await()
coroutineScope {
<caret>async { 42 }.await()
}
}
@@ -1,8 +1,10 @@
// WITH_RUNTIME
// FIX: Merge call chain to 'withContext(DefaultDispatcher)'
// FIX: Merge call chain to 'withContext(Default)'
package kotlinx.coroutines
suspend fun test() {
withContext(DefaultDispatcher) { 42 }
coroutineScope {
withContext(Dispatchers.Default) { 42 }
}
}
@@ -3,5 +3,7 @@
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
<caret>async(ctx, CoroutineStart.LAZY) { 42 }.await()
coroutineScope {
<caret>async(ctx, CoroutineStart.LAZY) { 42 }.await()
}
}
@@ -3,5 +3,7 @@
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
withContext(ctx, CoroutineStart.LAZY) { 42 }
coroutineScope {
withContext(ctx, CoroutineStart.LAZY) { 42 }
}
}
@@ -4,5 +4,7 @@
package kotlinx.coroutines
suspend fun test(ctx: CoroutineContext) {
<caret>async(start = CoroutineStart.LAZY) { 42 }.await()
coroutineScope {
<caret>async(start = CoroutineStart.LAZY) { 42 }.await()
}
}
@@ -2007,6 +2007,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt");
}
@TestMetadata("explicitScope.kt")
public void testExplicitScope() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/explicitScope.kt");
}
@TestMetadata("globalScope.kt")
public void testGlobalScope() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/globalScope.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt");
@@ -2017,11 +2027,6 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt");
}
@TestMetadata("withParent.kt")
public void testWithParent() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt");
}
@TestMetadata("withStartAndContext.kt")
public void testWithStartAndContext() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt");