1.3 migration: make inspections working for non-experimental coroutines

This commit is contained in:
Mikhail Glukhikh
2018-07-23 18:55:51 +03:00
parent 76b9b579bc
commit cc1d9e88d5
16 changed files with 225 additions and 14 deletions
@@ -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<FqName> = shortNames.mapTo(mutableSetOf()) { FqName("$COROUTINE_PACKAGE.$it") }
private val fqNamesExperimental: Set<FqName> = 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")
}
}
@@ -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"
@@ -0,0 +1,61 @@
package kotlinx.coroutines.experimental
interface Deferred<T> {
suspend fun await(): T
}
interface CoroutineContext
object DefaultContext : CoroutineContext
enum class CoroutineStart {
DEFAULT,
LAZY,
ATOMIC,
UNDISPATCHED
}
interface Job
fun <T> async(
context: CoroutineContext = DefaultContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
f: suspend () -> T
): Deferred<T> {
TODO()
}
fun test() {
async { 42 }
}
fun useIt(d: Deferred<Int>) {}
fun falsePositives() {
async { 3 }.await()
val res = async { 13 }
useIt(async { 7 })
}
class User
interface DbHandler {
fun getUser(id: Long): Deferred<User>
fun doStuff(): Deferred<Unit>
}
fun DbHandler.test() {
getUser(42L)
val user = getUser(42L).await()
doStuff()
doStuff().await()
}
operator fun Deferred<Int>.unaryPlus() = this
operator fun Deferred<Int>.plus(arg: Int) = this
fun moreFalsePositives() {
+(async { 0 })
async { -1 } + 1
}
@@ -26,4 +26,31 @@
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
<description>Deferred result is never used</description>
</problem>
<problem>
<file>experimental.kt</file>
<line>30</line>
<module>light_idea_test_case</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="temp:///src/experimental.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
<description>Deferred result is never used</description>
</problem>
<problem>
<file>experimental.kt</file>
<line>49</line>
<module>light_idea_test_case</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="temp:///src/experimental.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
<description>Deferred result is never used</description>
</problem>
<problem>
<file>experimental.kt</file>
<line>51</line>
<module>light_idea_test_case</module>
<package>&lt;default&gt;</package>
<entry_point TYPE="file" FQNAME="temp:///src/experimental.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Deferred result is never used</problem_class>
<description>Deferred result is never used</description>
</problem>
</problems>
@@ -1,4 +1,4 @@
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -0,0 +1,48 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
interface Deferred<T> {
suspend fun await(): T
}
interface CoroutineContext
object DefaultDispatcher : CoroutineContext
enum class CoroutineStart {
DEFAULT,
LAZY,
ATOMIC,
UNDISPATCHED
}
interface Job
fun <T> async(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
f: suspend () -> T
): Deferred<T> {
TODO()
}
fun <T> runBlocking(
context: CoroutineContext = DefaultDispatcher,
f: suspend () -> T
) {
TODO()
}
suspend fun <T> withContext(
context: CoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
f: suspend () -> T
) {
TODO()
}
suspend fun test(ctx: CoroutineContext) {
<caret>async(ctx) { 42 }.await()
}
@@ -0,0 +1,48 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
interface Deferred<T> {
suspend fun await(): T
}
interface CoroutineContext
object DefaultDispatcher : CoroutineContext
enum class CoroutineStart {
DEFAULT,
LAZY,
ATOMIC,
UNDISPATCHED
}
interface Job
fun <T> async(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
f: suspend () -> T
): Deferred<T> {
TODO()
}
fun <T> runBlocking(
context: CoroutineContext = DefaultDispatcher,
f: suspend () -> T
) {
TODO()
}
suspend fun <T> withContext(
context: CoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
f: suspend () -> T
) {
TODO()
}
suspend fun test(ctx: CoroutineContext) {
withContext(ctx) { 42 }
}
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,7 +1,7 @@
// PROBLEM: none
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,6 +1,6 @@
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -1,7 +1,7 @@
// PROBLEM: none
// WITH_RUNTIME
package kotlinx.coroutines.experimental
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
@@ -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");