diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/DeferredResultUnusedInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/DeferredResultUnusedInspection.kt index 084c04333db..b6546124604 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/DeferredResultUnusedInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/DeferredResultUnusedInspection.kt @@ -23,10 +23,11 @@ class DeferredResultUnusedInspection(@JvmField var standardOnly: Boolean = true) callChecker = fun(resolvedCall, inspection): Boolean { if (inspection !is DeferredResultUnusedInspection) return false return if (inspection.standardOnly) { - resolvedCall.resultingDescriptor.fqNameOrNull() in fqNames + resolvedCall.resultingDescriptor.fqNameOrNull() in fqNamesAll } else { val returnTypeClassifier = resolvedCall.resultingDescriptor.returnType?.constructor?.declarationDescriptor - returnTypeClassifier?.importableFqName == deferred + val importableFqName = returnTypeClassifier?.importableFqName + importableFqName == deferred || importableFqName == deferredExperimental } } ) { @@ -47,6 +48,12 @@ class DeferredResultUnusedInspection(@JvmField var standardOnly: Boolean = true) private val fqNames: Set = shortNames.mapTo(mutableSetOf()) { FqName("$COROUTINE_PACKAGE.$it") } + private val fqNamesExperimental: Set = shortNames.mapTo(mutableSetOf()) { FqName("$COROUTINE_EXPERIMENTAL_PACKAGE.$it") } + + private val fqNamesAll = fqNames + fqNamesExperimental + private val deferred = FqName("$COROUTINE_PACKAGE.Deferred") + + private val deferredExperimental = FqName("$COROUTINE_EXPERIMENTAL_PACKAGE.Deferred") } } \ No newline at end of file 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 711f3ac6964..642a4c07cff 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/RedundantAsyncInspection.kt @@ -36,7 +36,13 @@ class RedundantAsyncInspection : AbstractCallChainChecker() { if (defaultContext!! && !defaultStart!!) return var replacement = conversion.replacement - if (defaultContext!! && defaultStart!!) replacement += "($defaultAsyncArgument)" + if (defaultContext!! && defaultStart!!) { + if (conversion === conversions[0]) { + replacement += "($defaultAsyncArgument)" + } else { + replacement += "($defaultAsyncArgumentExperimental)" + } + } val descriptor = holder.manager.createProblemDescriptor( expression, @@ -57,11 +63,20 @@ class RedundantAsyncInspection : AbstractCallChainChecker() { "$COROUTINE_PACKAGE.async", "$COROUTINE_PACKAGE.Deferred.await", "$COROUTINE_PACKAGE.withContext" + ), + Conversion( + "$COROUTINE_EXPERIMENTAL_PACKAGE.async", + "$COROUTINE_EXPERIMENTAL_PACKAGE.Deferred.await", + "$COROUTINE_EXPERIMENTAL_PACKAGE.withContext" ) ) - private val defaultAsyncArgument = "$COROUTINE_PACKAGE.DefaultDispatcher" + private const val defaultAsyncArgument = "$COROUTINE_PACKAGE.DefaultDispatcher" + + private const val defaultAsyncArgumentExperimental = "$COROUTINE_EXPERIMENTAL_PACKAGE.DefaultDispatcher" } } -internal const val COROUTINE_PACKAGE = "kotlinx.coroutines.experimental" +internal const val COROUTINE_PACKAGE = "kotlinx.coroutines" + +internal const val COROUTINE_EXPERIMENTAL_PACKAGE = "kotlinx.coroutines.experimental" diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/experimental.kt b/idea/testData/inspections/coroutines/asyncResultUnused/experimental.kt new file mode 100644 index 00000000000..fb94b6f127d --- /dev/null +++ b/idea/testData/inspections/coroutines/asyncResultUnused/experimental.kt @@ -0,0 +1,61 @@ +package kotlinx.coroutines.experimental + +interface Deferred { + suspend fun await(): T +} + +interface CoroutineContext + +object DefaultContext : CoroutineContext + +enum class CoroutineStart { + DEFAULT, + LAZY, + ATOMIC, + UNDISPATCHED +} + +interface Job + +fun async( + context: CoroutineContext = DefaultContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + parent: Job? = null, + f: suspend () -> T +): Deferred { + TODO() +} + +fun test() { + async { 42 } +} + +fun useIt(d: Deferred) {} + +fun falsePositives() { + async { 3 }.await() + val res = async { 13 } + useIt(async { 7 }) +} + +class User + +interface DbHandler { + fun getUser(id: Long): Deferred + fun doStuff(): Deferred +} + +fun DbHandler.test() { + getUser(42L) + val user = getUser(42L).await() + doStuff() + doStuff().await() +} + +operator fun Deferred.unaryPlus() = this +operator fun Deferred.plus(arg: Int) = this + +fun moreFalsePositives() { + +(async { 0 }) + async { -1 } + 1 +} diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml index 0e259e03f44..47430afa5e8 100644 --- a/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml +++ b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml @@ -26,4 +26,31 @@ Deferred result is never used Deferred result is never used + + experimental.kt + 30 + light_idea_test_case + <default> + + Deferred result is never used + Deferred result is never used + + + experimental.kt + 49 + light_idea_test_case + <default> + + Deferred result is never used + Deferred result is never used + + + experimental.kt + 51 + light_idea_test_case + <default> + + Deferred result is never used + Deferred result is never used + \ No newline at end of file diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt b/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt index fb94b6f127d..69a5ec60381 100644 --- a/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt +++ b/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt @@ -1,4 +1,4 @@ -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt new file mode 100644 index 00000000000..3a2631859c4 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt @@ -0,0 +1,48 @@ +// WITH_RUNTIME + +package kotlinx.coroutines.experimental + +interface Deferred { + suspend fun await(): T +} + +interface CoroutineContext + +object DefaultDispatcher : CoroutineContext + +enum class CoroutineStart { + DEFAULT, + LAZY, + ATOMIC, + UNDISPATCHED +} + +interface Job + +fun async( + context: CoroutineContext = DefaultDispatcher, + start: CoroutineStart = CoroutineStart.DEFAULT, + parent: Job? = null, + f: suspend () -> T +): Deferred { + TODO() +} + +fun runBlocking( + context: CoroutineContext = DefaultDispatcher, + f: suspend () -> T +) { + TODO() +} + +suspend fun withContext( + context: CoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + f: suspend () -> T +) { + TODO() +} + +suspend fun test(ctx: CoroutineContext) { + async(ctx) { 42 }.await() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt.after new file mode 100644 index 00000000000..9c954a36e59 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt.after @@ -0,0 +1,48 @@ +// WITH_RUNTIME + +package kotlinx.coroutines.experimental + +interface Deferred { + suspend fun await(): T +} + +interface CoroutineContext + +object DefaultDispatcher : CoroutineContext + +enum class CoroutineStart { + DEFAULT, + LAZY, + ATOMIC, + UNDISPATCHED +} + +interface Job + +fun async( + context: CoroutineContext = DefaultDispatcher, + start: CoroutineStart = CoroutineStart.DEFAULT, + parent: Job? = null, + f: suspend () -> T +): Deferred { + TODO() +} + +fun runBlocking( + context: CoroutineContext = DefaultDispatcher, + f: suspend () -> T +) { + TODO() +} + +suspend fun withContext( + context: CoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + f: suspend () -> T +) { + TODO() +} + +suspend fun test(ctx: CoroutineContext) { + withContext(ctx) { 42 } +} \ 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 3a2631859c4..c25294b7029 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after index 9c954a36e59..16439deb0be 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt.after @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt index b2640fe1326..029c3f14adc 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after index 8b764961044..c18eed7f57d 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/simplest.kt.after @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt index 6bdcd5913d4..07858dc80ff 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withParent.kt @@ -1,7 +1,7 @@ // PROBLEM: none // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt index d3329844581..f1dcb86f5ec 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after index 3419f99fe67..0296cae6f75 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartAndContext.kt.after @@ -1,6 +1,6 @@ // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt index d260908f12c..8ac1d641c9e 100644 --- a/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt +++ b/idea/testData/inspectionsLocal/coroutines/redundantAsync/withStartNoContext.kt @@ -1,7 +1,7 @@ // PROBLEM: none // WITH_RUNTIME -package kotlinx.coroutines.experimental +package kotlinx.coroutines interface Deferred { suspend fun await(): T diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 119134314fa..057f67b6632 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -1358,6 +1358,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/coroutines/redundantAsync"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); } + @TestMetadata("experimental.kt") + public void testExperimental() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/experimental.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("idea/testData/inspectionsLocal/coroutines/redundantAsync/simple.kt");