diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractResultUnusedChecker.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractResultUnusedChecker.kt
new file mode 100644
index 00000000000..272cb4d8441
--- /dev/null
+++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractResultUnusedChecker.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+
+abstract class AbstractResultUnusedChecker(
+ private val expressionChecker: (KtExpression) -> Boolean,
+ private val callChecker: (ResolvedCall<*>) -> Boolean
+) : AbstractKotlinInspection() {
+ protected fun check(expression: KtExpression): Boolean {
+ // Check whatever possible by PSI
+ if (!expressionChecker(expression)) return false
+ var parent: PsiElement? = expression.parent
+ while (parent != null) {
+ if (parent is KtBlockExpression || parent is KtFunction || parent is KtFile) break
+ if (parent is KtValueArgument) return false
+ if (parent is KtBinaryExpression && parent.operationToken in KtTokens.ALL_ASSIGNMENTS) return false
+ parent = parent.parent
+ }
+ // Then check by call
+ val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_CFA)
+ if (expression.isUsedAsExpression(context)) return false
+ val resolvedCall = expression.getResolvedCall(context) ?: return false
+ return callChecker(resolvedCall)
+ }
+}
\ No newline at end of file
diff --git a/idea/resources/inspectionDescriptions/AsyncResultUnused.html b/idea/resources/inspectionDescriptions/AsyncResultUnused.html
new file mode 100644
index 00000000000..c6a81a7035d
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/AsyncResultUnused.html
@@ -0,0 +1,6 @@
+
+
+This inspection reports async call that is never used,
+so all actions inside async are never executed.
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 136c9eefcd5..a4112b7ca44 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2846,7 +2846,7 @@
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.172 b/idea/src/META-INF/plugin.xml.172
index 9f05da7b396..118f58e7470 100644
--- a/idea/src/META-INF/plugin.xml.172
+++ b/idea/src/META-INF/plugin.xml.172
@@ -2854,6 +2854,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.173 b/idea/src/META-INF/plugin.xml.173
index 1f4a6ee522e..be5da4a9c14 100644
--- a/idea/src/META-INF/plugin.xml.173
+++ b/idea/src/META-INF/plugin.xml.173
@@ -2854,6 +2854,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.182 b/idea/src/META-INF/plugin.xml.182
index d6451cde4ec..89eedfb0cf3 100644
--- a/idea/src/META-INF/plugin.xml.182
+++ b/idea/src/META-INF/plugin.xml.182
@@ -2855,6 +2855,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.as31 b/idea/src/META-INF/plugin.xml.as31
index 112fcb64691..53d029d932b 100644
--- a/idea/src/META-INF/plugin.xml.as31
+++ b/idea/src/META-INF/plugin.xml.as31
@@ -2854,6 +2854,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32
index a0af09b96e5..facbd2ef7de 100644
--- a/idea/src/META-INF/plugin.xml.as32
+++ b/idea/src/META-INF/plugin.xml.as32
@@ -2854,6 +2854,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/AsyncResultUnusedInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/AsyncResultUnusedInspection.kt
new file mode 100644
index 00000000000..a649633c782
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/AsyncResultUnusedInspection.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.coroutines
+
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.idea.inspections.AbstractResultUnusedChecker
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.callExpressionVisitor
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
+
+class AsyncResultUnusedInspection : AbstractResultUnusedChecker(
+ expressionChecker = fun(expression): Boolean =
+ expression is KtCallExpression && expression.calleeExpression?.text == "async",
+ callChecker = fun(resolvedCall): Boolean =
+ resolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == "kotlinx.coroutines.experimental.async"
+) {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor =
+ callExpressionVisitor(fun(expression) {
+ if (!check(expression)) return
+ holder.registerProblem(expression.calleeExpression ?: expression, "Result of 'async' is never used")
+ })
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml
new file mode 100644
index 00000000000..e77788b5f3b
--- /dev/null
+++ b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/expected.xml
@@ -0,0 +1,11 @@
+
+
+ simple.kt
+ 30
+ light_idea_test_case
+ <default>
+
+ Result of 'async' is not used
+ Result of 'async' is never used
+
+
\ No newline at end of file
diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/inspections.test b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/inspections.test
new file mode 100644
index 00000000000..4b6b3d052a1
--- /dev/null
+++ b/idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.coroutines.AsyncResultUnusedInspection
diff --git a/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt b/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt
new file mode 100644
index 00000000000..07c01df416f
--- /dev/null
+++ b/idea/testData/inspections/coroutines/asyncResultUnused/simple.kt
@@ -0,0 +1,39 @@
+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 })
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index c38f2fb3905..7795fc663e0 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -139,6 +139,11 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
runTest("idea/testData/inspections/copyWithoutNamedArguments/inspectionData/inspections.test");
}
+ @TestMetadata("coroutines/asyncResultUnused/inspectionData/inspections.test")
+ public void testCoroutines_asyncResultUnused_inspectionData_Inspections_test() throws Exception {
+ runTest("idea/testData/inspections/coroutines/asyncResultUnused/inspectionData/inspections.test");
+ }
+
@TestMetadata("dataClassPrivateConstructor/inspectionData/inspections.test")
public void testDataClassPrivateConstructor_inspectionData_Inspections_test() throws Exception {
runTest("idea/testData/inspections/dataClassPrivateConstructor/inspectionData/inspections.test");