diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index 6a05a337bab..260a9236541 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -3173,6 +3173,15 @@ language="kotlin" /> + + diff --git a/idea/resources/inspectionDescriptions/SuspendFunctionOnCoroutineScope.html b/idea/resources/inspectionDescriptions/SuspendFunctionOnCoroutineScope.html new file mode 100644 index 00000000000..0401a74ba96 --- /dev/null +++ b/idea/resources/inspectionDescriptions/SuspendFunctionOnCoroutineScope.html @@ -0,0 +1,17 @@ + + +This inspection reports calls & accesses of CoroutineScope extensions or members +inside suspend functions with CoroutineScope receiver. + +Both suspend functions and CoroutineScope members & extensions have access to coroutineContext. +When some function is simultaneously suspend and has CoroutineScope receiver, +it has ambiguous access to CoroutineContext: +first via kotlin.coroutines.coroutineContext and second via CoroutineScope.coroutineContext, +and two these contexts are different in general case. +So when we call some CoroutineScope extension or access coroutineContext from such a function, +it's unclear which from these two context do we have in mind. + +Normal ways to fix this are to wrap suspicious call inside coroutineScope { ... } or +to get rid of CoroutineScope function receiver. + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedReceiverParameterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedReceiverParameterInspection.kt index 0024951fd32..f6c9449146c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedReceiverParameterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedReceiverParameterInspection.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature import org.jetbrains.kotlin.idea.refactoring.explicateAsTextForReceiver import org.jetbrains.kotlin.idea.refactoring.getThisLabelName -import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.getThisReceiverOwner import org.jetbrains.kotlin.lexer.KtTokens @@ -119,45 +118,53 @@ class UnusedReceiverParameterInspection : AbstractKotlinInspection() { receiverTypeReference, KotlinBundle.message("unused.receiver.parameter"), ProblemHighlightType.LIKE_UNUSED_SYMBOL, - MyQuickFix(inSameClass) + RemoveReceiverFix(inSameClass) ) } } } - private class MyQuickFix(private val inSameClass: Boolean) : LocalQuickFix { - override fun getName(): String = KotlinBundle.message("unused.receiver.parameter.remove") - - private fun configureChangeSignature() = object : KotlinChangeSignatureConfiguration { - override fun performSilently(affectedFunctions: Collection) = true - override fun configure(originalDescriptor: KotlinMethodDescriptor) = originalDescriptor.modify { it.removeParameter(0) } - } + class RemoveReceiverFix(private val inSameClass: Boolean) : LocalQuickFix { + override fun getName(): String = actionName override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val element = descriptor.psiElement as? KtTypeReference ?: return if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return - val function = element.parent as? KtCallableDeclaration ?: return - val callableDescriptor = function.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: return - - val typeParameters = RemoveUnusedFunctionParameterFix.typeParameters(element) - if (inSameClass) { - runWriteAction { - val explicateAsTextForReceiver = callableDescriptor.explicateAsTextForReceiver() - function.forEachDescendantOfType { - if (it.text == explicateAsTextForReceiver) it.labelQualifier?.delete() - } - function.setReceiverTypeReference(null) - } - } else { - runChangeSignature(project, callableDescriptor, configureChangeSignature(), element, name) - } - RemoveUnusedFunctionParameterFix.runRemoveUnusedTypeParameters(typeParameters) + apply(element, project, inSameClass) } - override fun getFamilyName(): String = name + override fun getFamilyName(): String = actionName override fun startInWriteAction() = false + + companion object { + private val actionName = KotlinBundle.message("unused.receiver.parameter.remove") + + private fun configureChangeSignature() = object : KotlinChangeSignatureConfiguration { + override fun performSilently(affectedFunctions: Collection) = true + override fun configure(originalDescriptor: KotlinMethodDescriptor) = originalDescriptor.modify { it.removeParameter(0) } + } + + fun apply(element: KtTypeReference, project: Project, inSameClass: Boolean = false) { + val function = element.parent as? KtCallableDeclaration ?: return + val callableDescriptor = function.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: return + + val typeParameters = RemoveUnusedFunctionParameterFix.typeParameters(element) + if (inSameClass) { + runWriteAction { + val explicateAsTextForReceiver = callableDescriptor.explicateAsTextForReceiver() + function.forEachDescendantOfType { + if (it.text == explicateAsTextForReceiver) it.labelQualifier?.delete() + } + function.setReceiverTypeReference(null) + } + } else { + runChangeSignature(project, callableDescriptor, configureChangeSignature(), element, actionName) + } + RemoveUnusedFunctionParameterFix.runRemoveUnusedTypeParameters(typeParameters) + } + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/SuspendFunctionOnCoroutineScopeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/SuspendFunctionOnCoroutineScopeInspection.kt new file mode 100644 index 00000000000..38db77c856e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/coroutines/SuspendFunctionOnCoroutineScopeInspection.kt @@ -0,0 +1,216 @@ +/* + * 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.codeInsight.FileModificationService +import com.intellij.codeInspection.* +import com.intellij.codeInspection.ProblemHighlightType.* +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection +import org.jetbrains.kotlin.idea.inspections.UnusedReceiverParameterInspection +import org.jetbrains.kotlin.idea.intentions.ConvertReceiverToParameterIntention +import org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +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.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType + +class SuspendFunctionOnCoroutineScopeInspection : AbstractKotlinInspection() { + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return namedFunctionVisitor(fun(function: KtNamedFunction) { + if (!function.hasModifier(KtTokens.SUSPEND_KEYWORD)) return + if (!function.hasBody()) return + + val context = function.analyzeWithContent() + val descriptor = context[BindingContext.FUNCTION, function] ?: return + val (extensionOfCoroutineScope, memberOfCoroutineScope) = with(descriptor) { + extensionReceiverParameter.ofCoroutineScopeType() to dispatchReceiverParameter.ofCoroutineScopeType() + } + if (!extensionOfCoroutineScope && !memberOfCoroutineScope) return + + fun DeclarationDescriptor.isReceiverOfAnalyzedFunction(): Boolean { + if (extensionOfCoroutineScope && this == descriptor) return true + if (memberOfCoroutineScope && this == descriptor.containingDeclaration) return true + return false + } + + fun checkSuspiciousReceiver(receiver: ReceiverValue, problemExpression: KtExpression) { + when (receiver) { + is ImplicitReceiver -> if (!receiver.declarationDescriptor.isReceiverOfAnalyzedFunction()) return + is ExpressionReceiver -> { + val receiverThisExpression = receiver.expression as? KtThisExpression ?: return + if (receiverThisExpression.getTargetLabel() != null) { + val instanceReference = receiverThisExpression.instanceReference + if (context[BindingContext.REFERENCE_TARGET, instanceReference]?.isReceiverOfAnalyzedFunction() != true) return + } + } + } + val fixes = mutableListOf() + val reportElement = (problemExpression as? KtCallExpression)?.calleeExpression ?: problemExpression + holder.registerProblem( + reportElement, + MESSAGE, + GENERIC_ERROR_OR_WARNING, + WrapWithCoroutineScopeFix(removeReceiver = false, wrapCallOnly = true) + ) + fixes += WrapWithCoroutineScopeFix(removeReceiver = extensionOfCoroutineScope, wrapCallOnly = false) + val file = function.containingKtFile + if (extensionOfCoroutineScope) { + fixes += IntentionWrapper(ConvertReceiverToParameterIntention(), file) + } + if (memberOfCoroutineScope) { + val containingDeclaration = function.containingClassOrObject + if (containingDeclaration is KtClass && !containingDeclaration.isInterface() && function.hasBody()) { + fixes += IntentionWrapper(MoveMemberToCompanionObjectIntention(), file) + } + } + + holder.registerProblem( + with(function) { receiverTypeReference ?: nameIdentifier ?: funKeyword ?: this }, + MESSAGE, + GENERIC_ERROR_OR_WARNING, + *fixes.toTypedArray() + ) + } + + function.forEachDescendantOfType(fun(callExpression: KtCallExpression) { + val resolvedCall = callExpression.getResolvedCall(context) ?: return + val extensionReceiverParameter = resolvedCall.resultingDescriptor.extensionReceiverParameter ?: return + if (!extensionReceiverParameter.type.isCoroutineScope()) return + val extensionReceiver = resolvedCall.extensionReceiver ?: return + checkSuspiciousReceiver(extensionReceiver, callExpression) + }) + function.forEachDescendantOfType(fun(nameReferenceExpression: KtNameReferenceExpression) { + if (nameReferenceExpression.getReferencedName() != COROUTINE_CONTEXT) return + val resolvedCall = nameReferenceExpression.getResolvedCall(context) ?: return + if (resolvedCall.resultingDescriptor.fqNameSafe.asString() == "$COROUTINE_SCOPE.$COROUTINE_CONTEXT") { + val dispatchReceiver = resolvedCall.dispatchReceiver ?: return + checkSuspiciousReceiver(dispatchReceiver, nameReferenceExpression) + } + }) + }) + } + + private class WrapWithCoroutineScopeFix( + private val removeReceiver: Boolean, + private val wrapCallOnly: Boolean + ) : LocalQuickFix { + override fun getFamilyName(): String = "Wrap with coroutineScope" + + override fun getName(): String = + when { + removeReceiver && !wrapCallOnly -> "Remove receiver & wrap with 'coroutineScope { ... }'" + wrapCallOnly -> "Wrap call with 'coroutineScope { ... }'" + else -> "Wrap function body with 'coroutineScope { ... }'" + } + + override fun startInWriteAction() = false + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val problemElement = descriptor.psiElement ?: return + val function = problemElement.getNonStrictParentOfType() ?: return + val functionDescriptor = function.resolveToDescriptorIfAny() + if (!FileModificationService.getInstance().preparePsiElementForWrite(function)) return + val bodyExpression = function.bodyExpression + + fun getExpressionToWrapCall(): KtExpression? { + var result = problemElement as? KtExpression ?: return null + while (result.parent is KtQualifiedExpression || result.parent is KtCallExpression) { + result = result.parent as KtExpression + } + return result + } + + var expressionToWrap = when { + wrapCallOnly -> getExpressionToWrapCall() + else -> bodyExpression + } ?: return + if (functionDescriptor?.extensionReceiverParameter.ofCoroutineScopeType()) { + val context = function.analyzeWithContent() + expressionToWrap.forEachDescendantOfType { + val receiverExpression = it.receiverExpression as? KtThisExpression + val selectorExpression = it.selectorExpression + if (receiverExpression?.getTargetLabel() != null && selectorExpression != null) { + if (context[BindingContext.REFERENCE_TARGET, receiverExpression.instanceReference] == functionDescriptor) { + if (it === expressionToWrap) { + expressionToWrap = it.replaced(selectorExpression) + } else { + it.replace(selectorExpression) + } + } + } + } + } + + val factory = KtPsiFactory(function) + val blockExpression = function.bodyBlockExpression + project.executeWriteCommand(name, this) { + val result = when { + expressionToWrap != bodyExpression -> expressionToWrap.replaced( + factory.createExpressionByPattern("$COROUTINE_SCOPE_WRAPPER { $0 }", expressionToWrap) + ) + blockExpression == null -> bodyExpression.replaced( + factory.createExpressionByPattern("$COROUTINE_SCOPE_WRAPPER { $0 }", bodyExpression) + ) + else -> { + val bodyText = buildString { + for (statement in blockExpression.statements) { + append(statement.text) + } + } + blockExpression.replaced( + factory.createBlock("$COROUTINE_SCOPE_WRAPPER { $bodyText }") + ) + } + } + val reformatted = CodeStyleManager.getInstance(project).reformat(result) + ShortenReferences.DEFAULT.process(reformatted as KtElement) + } + + val receiverTypeReference = function.receiverTypeReference + if (removeReceiver && !wrapCallOnly && receiverTypeReference != null) { + UnusedReceiverParameterInspection.RemoveReceiverFix.apply(receiverTypeReference, project) + } + } + } + + companion object { + private const val COROUTINE_SCOPE = "kotlinx.coroutines.CoroutineScope" + + private const val COROUTINE_SCOPE_WRAPPER = "kotlinx.coroutines.coroutineScope" + + private const val COROUTINE_CONTEXT = "coroutineContext" + + private const val MESSAGE = "Ambiguous coroutineContext due to CoroutineScope receiver of suspend function" + + private fun KotlinType.isCoroutineScope(): Boolean = + constructor.declarationDescriptor?.fqNameSafe?.asString() == COROUTINE_SCOPE + + private fun ReceiverParameterDescriptor?.ofCoroutineScopeType(): Boolean { + if (this == null) return false + if (type.isCoroutineScope()) return true + return type.constructor.supertypes.reversed().any { it.isCoroutineScope() } + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/.inspection b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/.inspection new file mode 100644 index 00000000000..29c4137f563 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.coroutines.SuspendFunctionOnCoroutineScopeInspection diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt new file mode 100644 index 00000000000..f4c046a4244 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt @@ -0,0 +1,12 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun doSomething() {} + +fun foo() { + val scope = object : CoroutineScope { + suspend fun foo() { + async { doSomething() } + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt.after new file mode 100644 index 00000000000..0595234be69 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt.after @@ -0,0 +1,13 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun doSomething() {} + +fun foo() { + val scope = object : CoroutineScope { + suspend fun foo() { + coroutineScope { async { doSomething() } } + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt new file mode 100644 index 00000000000..c087092a5c6 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt @@ -0,0 +1,8 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +suspend fun CoroutineScope.foo() = async { calcSomething() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt.after new file mode 100644 index 00000000000..e490d772263 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt.after @@ -0,0 +1,8 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() = 42 + +suspend fun foo() = coroutineScope { async { calcSomething() } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt new file mode 100644 index 00000000000..e3e853a96c3 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt @@ -0,0 +1,15 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + async { + calcSomething() + } + async { + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt.after new file mode 100644 index 00000000000..959fc4265b6 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt.after @@ -0,0 +1,18 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + async { + calcSomething() + } + coroutineScope { + async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt new file mode 100644 index 00000000000..f0bdae6a073 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt @@ -0,0 +1,10 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(coroutineContext) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt.after new file mode 100644 index 00000000000..05fc051f9e3 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt.after @@ -0,0 +1,11 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext +import kotlinx.coroutines.coroutineScope + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(coroutineScope { coroutineContext }) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt new file mode 100644 index 00000000000..46d01e74ce2 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt @@ -0,0 +1,10 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(this@foo.coroutineContext) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt.after new file mode 100644 index 00000000000..05fc051f9e3 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt.after @@ -0,0 +1,11 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext +import kotlinx.coroutines.coroutineScope + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(coroutineScope { coroutineContext }) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt new file mode 100644 index 00000000000..2fe983b17a7 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt @@ -0,0 +1,10 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(this.coroutineContext) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt.after new file mode 100644 index 00000000000..9bb7a24a25a --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt.after @@ -0,0 +1,11 @@ +// FIX: Wrap call with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext +import kotlinx.coroutines.coroutineScope + +fun use(context: CoroutineContext) {} + +suspend fun CoroutineScope.foo() { + use(coroutineScope { this.coroutineContext }) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt new file mode 100644 index 00000000000..1330597d362 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt @@ -0,0 +1,8 @@ +// FIX: Convert receiver to parameter + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +suspend fun CoroutineScope.foo() = async { calcSomething() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt.after new file mode 100644 index 00000000000..fe239b6e549 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt.after @@ -0,0 +1,8 @@ +// FIX: Convert receiver to parameter + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +suspend fun foo(coroutineScope: CoroutineScope) = coroutineScope.async { calcSomething() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/coroutines.lib.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/coroutines.lib.kt new file mode 100644 index 00000000000..e7a34504351 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/coroutines.lib.kt @@ -0,0 +1,46 @@ +package kotlinx.coroutines + +interface Deferred { + 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 CoroutineScope.async( + context: CoroutineContext = Dispatchers.Default, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +): Deferred { + TODO() +} + +suspend fun withContext( + context: CoroutineContext, + block: suspend CoroutineScope.() -> T +): T { + TODO() +} + +suspend fun coroutineScope(block: suspend CoroutineScope.() -> R): R = GlobalScope.block() + +operator fun CoroutineContext.plus(other: CoroutineContext): CoroutineContext { + TODO() +} + diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt new file mode 100644 index 00000000000..3ac1d87d815 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt @@ -0,0 +1,10 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +suspend fun CoroutineScope.foo() { + val deferred = async { + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt.after new file mode 100644 index 00000000000..529611fc676 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt.after @@ -0,0 +1,13 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() = 42 + +suspend fun CoroutineScope.foo() { + val deferred = coroutineScope { + async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/inner.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/inner.kt new file mode 100644 index 00000000000..2e39394bdb8 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/inner.kt @@ -0,0 +1,15 @@ +// PROBLEM: none + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineContext + +fun use(context: CoroutineContext) {} + +abstract class MyCoroutineScope : CoroutineScope { + inner class Inner { + suspend fun foo() { + // Does not work yet (could work) + use(coroutineContext) + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt new file mode 100644 index 00000000000..acb67a76274 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt @@ -0,0 +1,21 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + async { + calcSomething() + calcSomething() // ... + + /* ... */ + + calcSomething() /* ... */ + + calcSomething() + if (true) // ... + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt.after new file mode 100644 index 00000000000..8d7fa39e8d9 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt.after @@ -0,0 +1,23 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +suspend fun foo() { + coroutineScope { + async { + calcSomething() + calcSomething() // ... + + /* ... */ + + calcSomething() /* ... */ + + calcSomething() + if (true) // ... + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt new file mode 100644 index 00000000000..b14e1e185b1 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt @@ -0,0 +1,10 @@ +// FIX: Move to companion object + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +abstract class MyCoroutineScope : CoroutineScope { + suspend fun foo() = async { calcSomething() } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt.after new file mode 100644 index 00000000000..1f814aa3aa8 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt.after @@ -0,0 +1,12 @@ +// FIX: Move to companion object + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +abstract class MyCoroutineScope : CoroutineScope { + companion object { + suspend fun foo(myCoroutineScope: MyCoroutineScope) = myCoroutineScope.async { calcSomething() } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt new file mode 100644 index 00000000000..aae26e4144c --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt @@ -0,0 +1,8 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +object MyCoroutineScope : CoroutineScope { + suspend fun foo() = async { calcSomething() } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt.after new file mode 100644 index 00000000000..a662c27832b --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt.after @@ -0,0 +1,9 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() = 42 + +object MyCoroutineScope : CoroutineScope { + suspend fun foo() = coroutineScope { async { calcSomething() } } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt new file mode 100644 index 00000000000..ff40e7e6e56 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt @@ -0,0 +1,12 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + async { + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt.after new file mode 100644 index 00000000000..be7f36297d9 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt.after @@ -0,0 +1,14 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +suspend fun foo() { + coroutineScope { + async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt new file mode 100644 index 00000000000..d3d887f0fbc --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt @@ -0,0 +1,12 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + this.async { + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt.after new file mode 100644 index 00000000000..767ce83ac6b --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt.after @@ -0,0 +1,14 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +suspend fun foo() { + coroutineScope { + this.async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt new file mode 100644 index 00000000000..11597e5715e --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt @@ -0,0 +1,10 @@ +// FIX: Wrap function body with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +abstract class MyCoroutineScope : CoroutineScope { + suspend fun foo() = this.async { calcSomething() } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt.after new file mode 100644 index 00000000000..992939cf175 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt.after @@ -0,0 +1,11 @@ +// FIX: Wrap function body with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() = 42 + +abstract class MyCoroutineScope : CoroutineScope { + suspend fun foo() = coroutineScope { this.async { calcSomething() } } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt new file mode 100644 index 00000000000..bab18f07276 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt @@ -0,0 +1,14 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +class My { + suspend fun CoroutineScope.foo() { + this@foo.async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt.after new file mode 100644 index 00000000000..183c4aaa1a1 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt.after @@ -0,0 +1,16 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +class My { + suspend fun foo() { + coroutineScope { + async { + calcSomething() + } + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisWrongLabeled.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisWrongLabeled.kt new file mode 100644 index 00000000000..b8c57633bda --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisWrongLabeled.kt @@ -0,0 +1,15 @@ +// PROBLEM: none +// DISABLE-ERRORS + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +class My { + suspend fun CoroutineScope.foo() { + this@My.async { + calcSomething() + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt new file mode 100644 index 00000000000..e5ecb019e0a --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt @@ -0,0 +1,17 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async + +fun calcSomething() {} + +suspend fun CoroutineScope.foo() { + async { + calcSomething() + } +} + +suspend fun test() { + GlobalScope.foo() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt.after new file mode 100644 index 00000000000..2fbb8cbe7fe --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt.after @@ -0,0 +1,18 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() {} + +suspend fun foo() { + coroutineScope { + async { + calcSomething() + } + } +} + +suspend fun test() { + foo() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt new file mode 100644 index 00000000000..9c8634feeb3 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt @@ -0,0 +1,12 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async + +fun calcSomething() = 42 + +suspend fun CoroutineScope.foo() { + val deferred = async { + calcSomething() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt.after b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt.after new file mode 100644 index 00000000000..06a130a9559 --- /dev/null +++ b/idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt.after @@ -0,0 +1,14 @@ +// FIX: Remove receiver & wrap with 'coroutineScope { ... }' + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +fun calcSomething() = 42 + +suspend fun foo() { + coroutineScope { + val deferred = async { + calcSomething() + } + } +} \ 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 02462b5f856..c6987ee8e43 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2239,6 +2239,114 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/coroutines/redundantRunCatching/simple.kt"); } } + + @TestMetadata("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionOnCoroutineScope extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInSuspendFunctionOnCoroutineScope() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("anonymous.kt") + public void testAnonymous() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/anonymous.kt"); + } + + @TestMetadata("body.kt") + public void testBody() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/body.kt"); + } + + @TestMetadata("call.kt") + public void testCall() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/call.kt"); + } + + @TestMetadata("context.kt") + public void testContext() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/context.kt"); + } + + @TestMetadata("contextWithLabeledThis.kt") + public void testContextWithLabeledThis() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithLabeledThis.kt"); + } + + @TestMetadata("contextWithThis.kt") + public void testContextWithThis() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/contextWithThis.kt"); + } + + @TestMetadata("convertReceiver.kt") + public void testConvertReceiver() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/convertReceiver.kt"); + } + + @TestMetadata("declaration.kt") + public void testDeclaration() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/declaration.kt"); + } + + @TestMetadata("inner.kt") + public void testInner() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/inner.kt"); + } + + @TestMetadata("lots.kt") + public void testLots() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/lots.kt"); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/member.kt"); + } + + @TestMetadata("object.kt") + public void testObject() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/object.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/simple.kt"); + } + + @TestMetadata("this.kt") + public void testThis() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/this.kt"); + } + + @TestMetadata("thisInMember.kt") + public void testThisInMember() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisInMember.kt"); + } + + @TestMetadata("thisLabeled.kt") + public void testThisLabeled() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisLabeled.kt"); + } + + @TestMetadata("thisWrongLabeled.kt") + public void testThisWrongLabeled() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/thisWrongLabeled.kt"); + } + + @TestMetadata("useSite.kt") + public void testUseSite() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/useSite.kt"); + } + + @TestMetadata("withVariable.kt") + public void testWithVariable() throws Exception { + runTest("idea/testData/inspectionsLocal/coroutines/suspendFunctionOnCoroutineScope/withVariable.kt"); + } + } } @TestMetadata("idea/testData/inspectionsLocal/delegationToVarProperty")