diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt index 642a4c07cff..14d70bf331c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt @@ -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" } diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/coroutines.lib.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/coroutines.lib.kt index 33def856c4f..a01293d42b7 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/coroutines.lib.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/coroutines.lib.kt @@ -6,7 +6,9 @@ interface Deferred { 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 async( - context: CoroutineContext = DefaultDispatcher, - start: CoroutineStart = CoroutineStart.DEFAULT, - parent: Job? = null, - f: suspend () -> T -): Deferred { - TODO() +interface CoroutineScope { + val coroutineContext: CoroutineContext get() = Dispatchers.Default } -fun runBlocking( - context: CoroutineContext = DefaultDispatcher, - f: suspend () -> T -) { +object GlobalScope : CoroutineScope + +fun CoroutineScope.async( + context: CoroutineContext = Dispatchers.Default, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +): Deferred { TODO() } suspend fun withContext( context: CoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - f: suspend () -> T + block: suspend CoroutineScope.() -> T ) { TODO() } +suspend fun coroutineScope(block: suspend CoroutineScope.() -> R): R = GlobalScope.block() + diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/explicitScope.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/explicitScope.kt new file mode 100644 index 00000000000..04de070bf97 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/explicitScope.kt @@ -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.async(ctx) { 42 }.await() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/globalScope.kt similarity index 55% rename from idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt rename to idea/testData/inspectionsLocal/coroutines/redundantAsync/globalScope.kt index 69aea1dc19b..7020fd8a82f 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/globalScope.kt @@ -1,8 +1,9 @@ -// PROBLEM: none // WITH_RUNTIME +// PROBLEM: none package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - async(parent = null) { 42 }.await() + // Does not work yet (1.3.11) + GlobalScope.async(ctx) { 42 }.await() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt index af46fe943e7..bf8fe8d7c1b 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt @@ -4,5 +4,7 @@ package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - async(ctx) { 42 }.await() + coroutineScope { + async(ctx) { 42 }.await() + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after index 0e9a5896243..f45fd770106 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after @@ -4,5 +4,7 @@ package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - withContext(ctx) { 42 } + coroutineScope { + withContext(ctx) { 42 } + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt index 5787f7b35e2..2bdfe30acda 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt @@ -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() { - async { 42 }.await() + coroutineScope { + async { 42 }.await() + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after index 367bd846556..e096e6b1311 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after @@ -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 } + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt index 2c4eeb479b4..a8c4abd51ae 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt @@ -3,5 +3,7 @@ package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - async(ctx, CoroutineStart.LAZY) { 42 }.await() + coroutineScope { + async(ctx, CoroutineStart.LAZY) { 42 }.await() + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after index 647cb453564..d98bbae7882 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after @@ -3,5 +3,7 @@ package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - withContext(ctx, CoroutineStart.LAZY) { 42 } + coroutineScope { + withContext(ctx, CoroutineStart.LAZY) { 42 } + } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt index 1f8510d5c6e..3aa82896939 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt @@ -4,5 +4,7 @@ package kotlinx.coroutines suspend fun test(ctx: CoroutineContext) { - async(start = CoroutineStart.LAZY) { 42 }.await() + coroutineScope { + async(start = CoroutineStart.LAZY) { 42 }.await() + } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index b7a0f8a0f72..400425147e4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -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");