override fun check()

in plugins/compose/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposableCallChecker.kt [112:287]


    override fun check(
        resolvedCall: ResolvedCall<*>,
        reportOn: PsiElement,
        context: CallCheckerContext,
    ) {
        val bindingContext = context.trace.bindingContext
        if (
            !resolvedCall.isComposableDelegateReference(bindingContext) &&
            !resolvedCall.isComposableInvocation()
        ) {
            checkInlineLambdaCall(resolvedCall, reportOn, context)
            return
        }

        warnOnUnstableNamedArguments(resolvedCall, context)

        var node: PsiElement? = reportOn
        loop@ while (node != null) {
            when (node) {
                is KtFunctionLiteral -> {
                    // keep going, as this is a "KtFunction", but we actually want the
                    // KtLambdaExpression
                }
                is KtLambdaExpression -> {
                    val descriptor = bindingContext[BindingContext.FUNCTION, node.functionLiteral]
                    if (descriptor == null) {
                        illegalCall(context, reportOn)
                        return
                    }
                    val composable = descriptor.isComposableCallable(bindingContext)
                    if (composable) return
                    val arg = getArgumentDescriptor(node.functionLiteral, bindingContext)
                    if (arg?.type?.hasDisallowComposableCallsAnnotation() == true) {
                        context.trace.record(
                            FrontendWritableSlices.LAMBDA_CAPABLE_OF_COMPOSER_CAPTURE,
                            descriptor,
                            false
                        )
                        context.trace.report(
                            ComposeErrors.CAPTURED_COMPOSABLE_INVOCATION.on(
                                reportOn,
                                arg,
                                arg.containingDeclaration
                            )
                        )
                        return
                    }

                    val isResolvedInline = bindingContext.get(
                        BindingContext.NEW_INFERENCE_IS_LAMBDA_FOR_OVERLOAD_RESOLUTION_INLINE,
                        node.functionLiteral
                    ) == true
                    val isInlined = isResolvedInline || isInlinedArgument(
                        node.functionLiteral,
                        bindingContext,
                        true
                    )
                    if (!isInlined) {
                        illegalCall(context, reportOn)
                        return
                    } else {
                        // since the function is inlined, we continue going up the PSI tree
                        // until we find a composable context. We also mark this lambda
                        context.trace.record(
                            FrontendWritableSlices.LAMBDA_CAPABLE_OF_COMPOSER_CAPTURE,
                            descriptor,
                            true
                        )
                    }
                }
                is KtTryExpression -> {
                    val tryKeyword = node.tryKeyword
                    if (
                        node.tryBlock.textRange.contains(reportOn.textRange) &&
                        tryKeyword != null
                    ) {
                        context.trace.report(
                            ComposeErrors.ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE.on(tryKeyword)
                        )
                    }
                }
                is KtFunction -> {
                    val descriptor = bindingContext[BindingContext.FUNCTION, node]
                    if (descriptor == null) {
                        illegalCall(context, reportOn)
                        return
                    }
                    val composable = descriptor.isComposableCallable(bindingContext)
                    if (!composable) {
                        illegalCall(context, reportOn, node.nameIdentifier ?: node)
                    }
                    if (descriptor.hasReadonlyComposableAnnotation()) {
                        // enforce that the original call was readonly
                        if (!resolvedCall.isReadOnlyComposableInvocation()) {
                            illegalCallMustBeReadonly(
                                context,
                                reportOn
                            )
                        }
                    }
                    return
                }
                is KtProperty -> {
                    // NOTE: since we're explicitly going down a different branch for
                    // KtPropertyAccessor, the ONLY time we make it into this branch is when the
                    // call was done in the initializer of the property/variable.
                    val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, node]

                    if (resolvedCall.isComposableDelegateOperator()) {
                        // The call is initializer for fields like `val foo by composableDelegate()`.
                        // Creating the property doesn't have any requirements from Compose side,
                        // we will recheck on the property call site instead.
                        if (
                            descriptor is VariableDescriptorWithAccessors &&
                            descriptor.isDelegated
                        ) {
                            if (descriptor.isVar) {
                                // setValue delegate is not allowed for now.
                                illegalComposableDelegate(context, reportOn)
                            }
                            if (descriptor is PropertyDescriptor &&
                                descriptor.getter?.hasComposableAnnotation() != true
                            ) {
                                composableExpected(context, node.nameIdentifier ?: node)
                            }
                            return
                        }
                    }

                    if (
                        descriptor !is LocalVariableDescriptor &&
                        node.annotationEntries.hasComposableAnnotation(bindingContext)
                    ) {
                        // composables shouldn't have initializers in the first place
                        illegalCall(context, reportOn)
                        return
                    }
                }
                is KtPropertyAccessor -> {
                    val property = node.property
                    val isComposable = node
                        .annotationEntries.hasComposableAnnotation(bindingContext)
                    if (!isComposable) {
                        illegalCall(context, reportOn, property.nameIdentifier ?: property)
                    }
                    val descriptor = bindingContext[BindingContext.PROPERTY_ACCESSOR, node]
                        ?: return
                    if (descriptor.hasReadonlyComposableAnnotation()) {
                        // enforce that the original call was readonly
                        if (!resolvedCall.isReadOnlyComposableInvocation()) {
                            illegalCallMustBeReadonly(
                                context,
                                reportOn
                            )
                        }
                    }
                    return
                }
                is KtCallableReferenceExpression -> {
                    illegalComposableFunctionReference(context, node)
                    return
                }
                is KtFile -> {
                    // if we've made it this far, the call was made in a non-composable context.
                    illegalCall(context, reportOn)
                    return
                }
                is KtClass -> {
                    // composable calls are never allowed in the initializers of a class
                    illegalCall(context, reportOn)
                    return
                }
            }
            node = node.parent as? KtElement
        }
    }