Introduce DeferredIsResultInspection #KT-25620 Fixed

This commit is contained in:
Mikhail Glukhikh
2018-11-23 18:09:25 +03:00
parent f4aad70b36
commit ca87e53f04
20 changed files with 440 additions and 161 deletions
@@ -3136,6 +3136,15 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.coroutines.DeferredIsResultInspection"
displayName="Function returning Deferred directly"
groupPath="Kotlin"
groupName="Style issues"
enabledByDefault="true"
level="WEAK WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -0,0 +1,8 @@
<html>
<body>
This inspection reports functions with <b>kotlinx.coroutines.Deferred</b> result.
Functions which use <b>Deferred</b> as return type should have a name with suffix <b>Async</b>.
Otherwise, it's recommended to turn a function into a suspend function and unwrap <b>Deferred</b>.
</body>
</html>
@@ -0,0 +1,86 @@
/*
* 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.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
abstract class AbstractIsResultInspection(
private val typeShortName: String,
private val typeFullName: String,
private val allowedSuffix: String,
private val allowedNames: Set<String>,
private val suggestedFunctionNameToCall: String,
private val shouldMakeSuspend: Boolean = false,
private val simplify: (KtExpression) -> Unit = {}
) : AbstractKotlinInspection() {
protected fun analyzeFunction(function: KtFunction, toReport: PsiElement, holder: ProblemsHolder) {
if (function is KtConstructor<*>) return
val returnTypeText = function.getReturnTypeReference()?.text
if (returnTypeText != null && typeShortName !in returnTypeText) return
val name = (function as? KtNamedFunction)?.nameAsName?.asString()
if (name in allowedNames) return
if (function is KtNamedFunction) {
val receiverTypeReference = function.receiverTypeReference
// Filter given type extensions
if (receiverTypeReference != null && typeShortName in receiverTypeReference.text) return
}
if (function is KtFunctionLiteral || returnTypeText == null) {
// Heuristics to save performance: check if something creates given type in function text
val text = function.bodyExpression?.text
if (text != null && allowedNames.none { it in text } && typeShortName !in text && allowedSuffix !in text) return
}
val descriptor = function.resolveToDescriptorIfAny() as? FunctionDescriptor ?: return
val returnType = descriptor.returnType ?: return
val returnTypeClass = returnType.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (returnTypeClass.fqNameSafe.asString() != typeFullName) return
if (name != null && name.endsWith(allowedSuffix)) {
analyzeFunctionWithAllowedSuffix(name, descriptor, toReport, holder)
} else {
holder.registerProblem(
toReport,
"Function returning $typeShortName with a name that does not end with $allowedSuffix",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
*listOfNotNull(
name?.let { RenameToFix("$it$allowedSuffix") },
AddCallOrUnwrapTypeFix(
withBody = function.hasBody(),
functionName = suggestedFunctionNameToCall,
typeName = typeShortName,
shouldMakeSuspend = shouldMakeSuspend,
simplify = simplify
)
).toTypedArray()
)
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
analyzeFunction(function, function.nameIdentifier ?: function.funKeyword ?: function, holder)
}
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
analyzeFunction(lambdaExpression.functionLiteral, lambdaExpression.functionLiteral.lBrace, holder)
}
}
}
open fun analyzeFunctionWithAllowedSuffix(name: String, descriptor: FunctionDescriptor, toReport: PsiElement, holder: ProblemsHolder) {}
}
@@ -0,0 +1,71 @@
/*
* 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.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.core.setType
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
class AddCallOrUnwrapTypeFix(
val withBody: Boolean,
val functionName: String,
val typeName: String,
val shouldMakeSuspend: Boolean,
val simplify: (KtExpression) -> Unit
) : LocalQuickFix {
override fun getName(): String =
if (withBody) "Add '.$functionName()' to function result (breaks use-sites!)"
else "Unwrap '$typeName' return type (breaks use-sites!)"
override fun getFamilyName(): String = name
private fun KtExpression.addCallAndSimplify(factory: KtPsiFactory) {
val newCallExpression = factory.createExpressionByPattern("$0.$functionName()", this)
val result = replaced(newCallExpression)
simplify(result)
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val function = descriptor.psiElement.getNonStrictParentOfType<KtFunction>() ?: return
val returnTypeReference = function.getReturnTypeReference()
val context = function.analyzeWithContent()
val functionDescriptor = context[BindingContext.FUNCTION, function] ?: return
if (shouldMakeSuspend) {
function.addModifier(KtTokens.SUSPEND_KEYWORD)
}
if (returnTypeReference != null) {
val returnType = functionDescriptor.returnType ?: return
val returnTypeArgument = returnType.arguments.firstOrNull()?.type ?: return
function.setType(returnTypeArgument)
}
if (!withBody) return
val factory = KtPsiFactory(project)
val bodyExpression = function.bodyExpression
bodyExpression?.forEachDescendantOfType<KtReturnExpression> {
if (it.getTargetFunctionDescriptor(context) == functionDescriptor) {
it.returnedExpression?.addCallAndSimplify(factory)
}
}
if (function is KtFunctionLiteral) {
val lastStatement = function.bodyExpression?.statements?.lastOrNull()
if (lastStatement != null && lastStatement !is KtReturnExpression) {
lastStatement.addCallAndSimplify(factory)
}
} else if (!function.hasBlockBody()) {
bodyExpression?.addCallAndSimplify(factory)
}
}
}
@@ -0,0 +1,24 @@
/*
* 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 org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtQualifiedExpression
class DeferredIsResultInspection : AbstractIsResultInspection(
typeShortName = "Deferred",
typeFullName = "kotlinx.coroutines.Deferred",
allowedSuffix = "Async",
allowedNames = setOf("async"),
suggestedFunctionNameToCall = "await",
shouldMakeSuspend = true,
simplify = fun(expression: KtExpression) {
val qualifiedExpression = expression as? KtQualifiedExpression ?: return
val redundantAsyncInspection = RedundantAsyncInspection()
val conversion = redundantAsyncInspection.generateConversion(qualifiedExpression) ?: return
redundantAsyncInspection.generateFix(conversion).apply(qualifiedExpression)
}
)
@@ -5,35 +5,25 @@
package org.jetbrains.kotlin.idea.inspections.coroutines
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.refactoring.rename.RenameProcessor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.setType
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
class DirectUseOfResultTypeInspection : AbstractKotlinInspection() {
class DirectUseOfResultTypeInspection : AbstractIsResultInspection(
typeShortName = SHORT_NAME,
typeFullName = "kotlin.Result",
allowedSuffix = CATCHING,
allowedNames = setOf("success", "failure", "runCatching"),
suggestedFunctionNameToCall = "getOrThrow"
) {
private fun MemberScope.hasCorrespondingNonCatchingFunction(
nameWithoutCatching: String,
@@ -76,130 +66,27 @@ class DirectUseOfResultTypeInspection : AbstractKotlinInspection() {
return false
}
private fun analyzeFunction(function: KtFunction, toReport: PsiElement, holder: ProblemsHolder) {
if (function is KtConstructor<*>) return
val returnTypeText = function.getReturnTypeReference()?.text
if (returnTypeText != null && SHORT_NAME !in returnTypeText) return
val name = (function as? KtNamedFunction)?.nameAsName?.asString()
// Filter names from stdlib
if (name in ALLOWED_NAMES) return
if (function is KtNamedFunction) {
val receiverTypeReference = function.receiverTypeReference
// Filter Result extensions
if (receiverTypeReference != null && SHORT_NAME in receiverTypeReference.text) return
}
if (function is KtFunctionLiteral || returnTypeText == null) {
// Heuristics to save performance
val text = function.bodyExpression?.text
// Check there is something creating Result in function text
if (text != null && ALLOWED_NAMES.none { it in text } && SHORT_NAME !in text && CATCHING !in text) return
}
val descriptor = function.resolveToDescriptorIfAny() as? FunctionDescriptor ?: return
override fun analyzeFunctionWithAllowedSuffix(
name: String,
descriptor: FunctionDescriptor,
toReport: PsiElement,
holder: ProblemsHolder
) {
val returnType = descriptor.returnType ?: return
val returnTypeClass = returnType.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (returnTypeClass.fqNameSafe.asString() != FULL_NAME) return
if (name != null && name.endsWith(CATCHING)) {
val nameWithoutCatching = name.substringBeforeLast(CATCHING)
if (descriptor.hasCorrespondingNonCatchingFunction(returnType, nameWithoutCatching) == false) {
val returnTypeArgument = returnType.arguments.firstOrNull()?.type
val typeName = returnTypeArgument?.constructor?.declarationDescriptor?.name?.asString() ?: "T"
holder.registerProblem(
toReport,
"Function '$name' returning '$SHORT_NAME<$typeName>' without the corresponding " +
"function '$nameWithoutCatching' returning '$typeName'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
}
} else {
val nameWithoutCatching = name.substringBeforeLast(CATCHING)
if (descriptor.hasCorrespondingNonCatchingFunction(returnType, nameWithoutCatching) == false) {
val returnTypeArgument = returnType.arguments.firstOrNull()?.type
val typeName = returnTypeArgument?.constructor?.declarationDescriptor?.name?.asString() ?: "T"
holder.registerProblem(
toReport,
"Function returning $SHORT_NAME with a name that does not end with $CATCHING",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
*listOfNotNull(
AddGetOrThrowFix(withBody = function.hasBody()),
name?.let { RenameToCatchingFix("$it$CATCHING") }
).toTypedArray()
"Function '$name' returning '$SHORT_NAME<$typeName>' without the corresponding " +
"function '$nameWithoutCatching' returning '$typeName'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
}
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
analyzeFunction(function, function.nameIdentifier ?: function.funKeyword ?: function, holder)
}
private const val SHORT_NAME = "Result"
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
analyzeFunction(lambdaExpression.functionLiteral, lambdaExpression.functionLiteral.lBrace, holder)
}
}
}
private class AddGetOrThrowFix(val withBody: Boolean) : LocalQuickFix {
override fun getName(): String =
if (withBody) "Add '.$GET_OR_THROW()' to function result (breaks use-sites!)"
else "Unwrap '$SHORT_NAME' return type (breaks use-sites!)"
override fun getFamilyName(): String = name
private fun KtExpression.addGetOrThrow(factory: KtPsiFactory) {
val getOrThrowExpression = factory.createExpressionByPattern("$0.$GET_OR_THROW()", this)
replace(getOrThrowExpression)
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val function = descriptor.psiElement.getNonStrictParentOfType<KtFunction>() ?: return
val returnTypeReference = function.getReturnTypeReference()
val context = function.analyze()
val functionDescriptor = context[BindingContext.FUNCTION, function] ?: return
if (returnTypeReference != null) {
val returnType = functionDescriptor.returnType ?: return
val returnTypeArgument = returnType.arguments.firstOrNull()?.type ?: return
function.setType(returnTypeArgument)
}
if (!withBody) return
val factory = KtPsiFactory(project)
val bodyExpression = function.bodyExpression
bodyExpression?.forEachDescendantOfType<KtReturnExpression> {
if (it.getTargetFunctionDescriptor(context) == functionDescriptor) {
it.returnedExpression?.addGetOrThrow(factory)
}
}
if (function is KtFunctionLiteral) {
val lastStatement = function.bodyExpression?.statements?.lastOrNull()
if (lastStatement != null && lastStatement !is KtReturnExpression) {
lastStatement.addGetOrThrow(factory)
}
} else if (!function.hasBlockBody()) {
bodyExpression?.addGetOrThrow(factory)
}
}
}
private class RenameToCatchingFix(val newName: String) : LocalQuickFix {
override fun getName(): String = "Rename to '$newName'"
override fun getFamilyName(): String = name
override fun startInWriteAction(): Boolean = false
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val function = descriptor.psiElement.getNonStrictParentOfType<KtFunction>() ?: return
RenameProcessor(project, function, newName, false, false).run()
}
}
companion object {
private const val SHORT_NAME = "Result"
private const val FULL_NAME = "kotlin.$SHORT_NAME"
private const val CATCHING = "Catching"
private const val GET_OR_THROW = "getOrThrow"
private val ALLOWED_NAMES = setOf("success", "failure", "runCatching")
}
}
private const val CATCHING = "Catching"
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class RedundantAsyncInspection : AbstractCallChainChecker() {
private fun generateConversion(expression: KtQualifiedExpression): Conversion? {
fun generateConversion(expression: KtQualifiedExpression): Conversion? {
var defaultContext: Boolean? = null
var defaultStart: Boolean? = null
@@ -63,38 +63,42 @@ class RedundantAsyncInspection : AbstractCallChainChecker() {
return conversion
}
fun generateFix(conversion: Conversion): SimplifyCallChainFix {
val contextArgument = conversion.additionalArgument
return SimplifyCallChainFix(conversion, removeReceiverOfFirstCall = true, runOptimizeImports = true) { callExpression ->
if (contextArgument != null) {
val call = callExpression.resolveToCall()
if (call != null) {
for (argument in callExpression.valueArguments) {
val mapping = call.getArgumentMapping(argument) as? ArgumentMatch ?: continue
if (mapping.valueParameter.name.asString() == CONTEXT_ARGUMENT_NAME) {
val name = argument.getArgumentName()?.asName
val expressionText = contextArgument + " + " + argument.getArgumentExpression()!!.text
argument.replace(
if (name == null) {
createArgument(expressionText)
} else {
createArgument("$name = $expressionText")
}
)
break
}
}
}
}
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
qualifiedExpressionVisitor(fun(expression) {
val conversion = generateConversion(expression) ?: return
val contextArgument = conversion.additionalArgument
val descriptor = holder.manager.createProblemDescriptor(
expression,
expression.firstCalleeExpression()!!.textRange.shiftRight(-expression.startOffset),
"Redundant 'async' call may be reduced to '${conversion.replacement}'",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
SimplifyCallChainFix(conversion, removeReceiverOfFirstCall = true, runOptimizeImports = true) { callExpression ->
if (contextArgument != null) {
val call = callExpression.resolveToCall()
if (call != null) {
for (argument in callExpression.valueArguments) {
val mapping = call.getArgumentMapping(argument) as? ArgumentMatch ?: continue
if (mapping.valueParameter.name.asString() == CONTEXT_ARGUMENT_NAME) {
val name = argument.getArgumentName()?.asName
val expressionText = contextArgument + " + " + argument.getArgumentExpression()!!.text
argument.replace(
if (name == null) {
createArgument(expressionText)
} else {
createArgument("$name = $expressionText")
}
)
break
}
}
}
}
}
generateFix(conversion)
)
holder.registerProblem(descriptor)
})
@@ -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.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import com.intellij.refactoring.rename.RenameProcessor
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
class RenameToFix(val newName: String) : LocalQuickFix {
override fun getName(): String = "Rename to '$newName'"
override fun getFamilyName(): String = name
override fun startInWriteAction(): Boolean = false
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val function = descriptor.psiElement.getNonStrictParentOfType<KtFunction>() ?: return
RenameProcessor(project, function, newName, false, false).run()
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.coroutines.DeferredIsResultInspection
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Unwrap 'Deferred' return type (breaks use-sites!)
package kotlinx.coroutines
interface My {
fun <caret>function(): Deferred<Int>
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Unwrap 'Deferred' return type (breaks use-sites!)
package kotlinx.coroutines
interface My {
suspend fun <caret>function(): Int
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
// FIX: Add '.await()' to function result (breaks use-sites!)
package kotlinx.coroutines
// TODO: this test contains strange formatting bug (see 0 -> return in *.after file). To be fixed.
fun <caret>myFunction(context: CoroutineContext, switch: Int): Deferred<Int> {
with (GlobalScope) {
when (switch) {
0 -> return async {
val x = 123
x * x
}
1 -> return async(context) { -1 }
else -> return async() { 9 }
}
}
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
// FIX: Add '.await()' to function result (breaks use-sites!)
package kotlinx.coroutines
// TODO: this test contains strange formatting bug (see 0 -> return in *.after file). To be fixed.
suspend fun myFunction(context: CoroutineContext, switch: Int): Int {
with (GlobalScope) {
when (switch) {
0 -> return withContext(Dispatchers.Default) {
val x = 123
x * x
}
1 -> return withContext(context) { -1 }
else -> return withContext(Dispatchers.Default) { 9 }
}
}
}
@@ -0,0 +1,46 @@
package kotlinx.coroutines
interface Deferred<T> {
suspend fun await(): T
}
interface CoroutineContext
object Dispatchers {
object Default : CoroutineContext
}
enum class CoroutineStart {
DEFAULT,
LAZY,
ATOMIC,
UNDISPATCHED
}
interface CoroutineScope {
val coroutineContext: CoroutineContext get() = Dispatchers.Default
}
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,
block: suspend CoroutineScope.() -> T
): T {
TODO()
}
suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R = GlobalScope.block()
operator fun CoroutineContext.plus(other: CoroutineContext): CoroutineContext {
TODO()
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Rename to 'myFunctionAsync'
package kotlinx.coroutines
fun <caret>myFunction(): Deferred<Int> {
return GlobalScope.async { 42 }
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Rename to 'myFunctionAsync'
package kotlinx.coroutines
fun myFunctionAsync(): Deferred<Int> {
return GlobalScope.async { 42 }
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Add '.await()' to function result (breaks use-sites!)
package kotlinx.coroutines
fun <caret>myFunction(): Deferred<Int> {
return GlobalScope.async { 42 }
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// FIX: Add '.await()' to function result (breaks use-sites!)
package kotlinx.coroutines
suspend fun myFunction(): Int {
return withContext(Dispatchers.Default) { 42 }
}
@@ -34,7 +34,7 @@ fun <T> CoroutineScope.async(
suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
) {
): T {
TODO()
}
@@ -1990,6 +1990,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/coroutines"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("idea/testData/inspectionsLocal/coroutines/deferredIsResult")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DeferredIsResult extends AbstractLocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
@TestMetadata("abstract.kt")
public void testAbstract() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/deferredIsResult/abstract.kt");
}
public void testAllFilesPresentInDeferredIsResult() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/coroutines/deferredIsResult"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("complex.kt")
public void testComplex() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/deferredIsResult/complex.kt");
}
@TestMetadata("rename.kt")
public void testRename() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/deferredIsResult/rename.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("idea/testData/inspectionsLocal/coroutines/deferredIsResult/simple.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/coroutines/directUseOfResultType")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)