override fun visitComposable()

in rules/common/src/main/kotlin/com/twitter/compose/rules/ComposeModifierReused.kt [23:68]


    override fun visitComposable(function: KtFunction, autoCorrect: Boolean, emitter: Emitter) {
        if (!function.emitsContent) return
        val composableBlockExpression = function.bodyBlockExpression ?: return
        val modifier = function.modifierParameter ?: return
        val initialName = modifier.name ?: return

        // Try to get all possible names by iterating on possible name reassignments until it's stable
        val modifierNames = composableBlockExpression.obtainAllModifierNames(initialName)

        // Find all composable-looking CALL_EXPRESSIONs that are using any of these modifier names
        composableBlockExpression.findChildrenByClass<KtCallExpression>()
            .filter { it.calleeExpression?.text?.first()?.isUpperCase() == true }
            .filter { it.isUsingModifiers(modifierNames) }
            .map { callExpression ->
                // To get an accurate count (and respecting if/when/whatever different branches)
                // we'll need to traverse upwards to [function] from each one of these usages
                // to see the real amount of usages.
                buildSet<KtCallExpression> {
                    var current: PsiElement = callExpression
                    while (current != composableBlockExpression) {
                        // If the current element is a CALL_EXPRESSION and using modifiers, log it
                        if (current is KtCallExpression && current.isUsingModifiers(modifierNames)) {
                            add(current)
                        }
                        // If any of the siblings also use any of these, we also log them.
                        // This is for the special case where only sibling composables reuse modifiers
                        addAll(
                            current.siblings()
                                .filterIsInstance<KtCallExpression>()
                                .filter { it.isUsingModifiers(modifierNames) },
                        )
                        current = current.parent
                    }
                }
            }
            // Any set with more than 1 item is interesting to us: means there is a rule violation
            .filter { it.size > 1 }
            // At this point we have all the grouping of violations, so we just need to extract all individual
            // items from them as we are no longer interested in the groupings, but their individual elements
            .flatten()
            // We don't want to double report
            .distinct()
            .forEach { callExpression ->
                emitter.report(callExpression, ModifierShouldBeUsedOnceOnly, false)
            }
    }