From 0b0a6d6ede0597bf2909377f09d1fd34240bf8ee Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Mon, 12 Apr 2021 18:14:33 +0300 Subject: [PATCH] Fix false positive ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL Previously added additional processing at findEnclosingSuspendFunction seems unnecessary anymore ^KT-43258 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++++ .../SuspensionPointInsideMutexLockChecker.kt | 4 +-- .../calls/checkers/coroutineCallChecker.kt | 26 +++---------------- .../calls/tower/ResolvedAtomCompleter.kt | 11 ++++++-- .../coroutines/suspendInvokeWithReceiver.kt | 13 ++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++++ 6 files changed, 39 insertions(+), 27 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 8d88377d837..1d1118275ed 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -5601,6 +5601,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/coroutines/suspendInvokeInsideWhen.kt"); } + @Test + @TestMetadata("suspendInvokeWithReceiver.kt") + public void testSuspendInvokeWithReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/coroutines/callableReference") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SuspensionPointInsideMutexLockChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SuspensionPointInsideMutexLockChecker.kt index 40863635b9e..7ae2f0be5b1 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SuspensionPointInsideMutexLockChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SuspensionPointInsideMutexLockChecker.kt @@ -26,7 +26,7 @@ class SuspensionPointInsideMutexLockChecker : CallChecker { if (descriptor !is FunctionDescriptor || !descriptor.isSuspend) return val enclosingSuspendFunctionSource = - findEnclosingSuspendFunction(context, resolvedCall.call.callElement)?.source?.getPsi() ?: return + findEnclosingSuspendFunction(context)?.source?.getPsi() ?: return // Search for `synchronized` call var parent = reportOn @@ -78,4 +78,4 @@ class SuspensionPointInsideMutexLockChecker : CallChecker { private fun reportProblem(context: CallCheckerContext, reportOn: PsiElement, resolvedCall: ResolvedCall<*>) { context.trace.report(ErrorsJvm.SUSPENSION_POINT_INSIDE_CRITICAL_SECTION.on(reportOn, resolvedCall.resultingDescriptor)) } -} \ No newline at end of file +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt index c6e2cfa6968..faac7147f77 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt @@ -14,13 +14,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtCodeFragment -import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtThisExpression import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.isCallableReference -import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.inline.InlineUtil @@ -52,28 +50,10 @@ fun PropertyDescriptor.isBuiltInCoroutineContext(languageVersionSettings: Langua private val ALLOWED_SCOPE_KINDS = setOf(LexicalScopeKind.FUNCTION_INNER_SCOPE, LexicalScopeKind.FUNCTION_HEADER_FOR_DESTRUCTURING) -fun findEnclosingSuspendFunction(context: CallCheckerContext, checkingCall: KtElement): FunctionDescriptor? { - /* - * If checking call isn't equal to call in resolution context, we should look at lexical scope from trace. - * It means there is a parent function analysis of which isn't completed yet - * and their lexical scope in the resolution context isn't recorded yet (but there is lexical scope with not completed descriptor in trace). - * Example (suggest that we're analyzing the last expression of lambda now): - * fun main() { - * runBlocking { - * retry { 1 } // `fun main` lexical scope in the resolution context, `runBlocking { ... }` one in the recorded in trace lexical scope - * } - * } - */ - val scope = if (context.resolutionContext !is CallResolutionContext<*> || context.resolutionContext.call.callElement == checkingCall) { - context.scope - } else { - context.trace.get(BindingContext.LEXICAL_SCOPE, checkingCall) ?: context.scope - } - - return scope.parentsWithSelf.firstOrNull { +fun findEnclosingSuspendFunction(context: CallCheckerContext): FunctionDescriptor? = + context.scope.parentsWithSelf.firstOrNull { it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS && it.ownerDescriptor.safeAs()?.isSuspend == true }?.cast()?.ownerDescriptor?.cast() -} object CoroutineSuspendCallChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { @@ -89,7 +69,7 @@ object CoroutineSuspendCallChecker : CallChecker { } val callElement = resolvedCall.call.callElement as KtExpression - val enclosingSuspendFunction = findEnclosingSuspendFunction(context, callElement) + val enclosingSuspendFunction = findEnclosingSuspendFunction(context) when { enclosingSuspendFunction != null -> { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt index da43256d386..fbc09746d58 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt @@ -117,7 +117,8 @@ class ResolvedAtomCompleter( clearPartiallyResolvedCall(resolvedCallAtom) - if (resolvedCallAtom.atom.psiKotlinCall is PSIKotlinCallForVariable) return null + val atom = resolvedCallAtom.atom + if (atom.psiKotlinCall is PSIKotlinCallForVariable) return null val allDiagnostics = diagnostics + diagnosticsFromPartiallyResolvedCall @@ -135,8 +136,14 @@ class ResolvedAtomCompleter( return resolvedCall } + val psiCallForResolutionContext = when (atom) { + // PARTIAL_CALL_RESOLUTION_CONTEXT has been written for the baseCall + is PSIKotlinCallForInvoke -> atom.baseCall.psiCall + else -> atom.psiKotlinCall.psiCall + } + val resolutionContextForPartialCall = - topLevelCallContext.trace[BindingContext.PARTIAL_CALL_RESOLUTION_CONTEXT, resolvedCallAtom.atom.psiKotlinCall.psiCall] + topLevelCallContext.trace[BindingContext.PARTIAL_CALL_RESOLUTION_CONTEXT, psiCallForResolutionContext] val callCheckerContext = if (resolutionContextForPartialCall != null) CallCheckerContext( diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt b/compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt new file mode 100644 index 00000000000..c48a25e21f9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt @@ -0,0 +1,13 @@ +// FIR_IDENTICAL +// SKIP_TXT +fun r(x: suspend () -> T): T = null!! + +fun nonReproducer1(): String = r { + MyObject.someProperty() +} + +object MyObject { + val someProperty: String = "TODO()" +} + +suspend operator fun String.invoke(): R = null!! diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 7b7dd6e3f22..eca0f962cc9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -5607,6 +5607,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/coroutines/suspendInvokeInsideWhen.kt"); } + @Test + @TestMetadata("suspendInvokeWithReceiver.kt") + public void testSuspendInvokeWithReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/coroutines/suspendInvokeWithReceiver.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/coroutines/callableReference") @TestDataPath("$PROJECT_ROOT")