Search code examples
androidstatic-analysisandroid-lint

Custom lint rule: How to fix "This annotation is not applicable to target 'expression'"


I am writing my first custom lint-detector for android/compose:

"Don't use the standard button, use our specifically styled custom button"


The detector itself is working as expected:

enter image description here


Lint suppression however doesn't work as expected: "This annotation is not applicable to target 'expression'"

enter image description here


I have to revert to a different scope to get this to work:

enter image description here


How can i fix my lint-rule in such a way, that i can target expressions?


My current (simplified) implementation:

class MissingDefaultDetector : Detector(), SourceCodeScanner {

    companion object {
        private const val old = "Button"

        @JvmField
        val ISSUE: Issue = Issue.create(
            id = "PreferDefaultComposables",
            briefDescription = "Prefer 'DefaultComposables'",
            explanation = "The default implementation should be used",
            category = Category.CORRECTNESS,
            priority = 6,
            severity = Severity.WARNING,
            implementation = Implementation(
                MissingDefaultDetector::class.java,
                Scope.JAVA_FILE_SCOPE
            )
        )
    }

    override fun getApplicableMethodNames(): List<String> = listOf(old)

    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
        context.report(
            issue = ISSUE,
            scope = node,
            location = context.getNameLocation(node),
            message = "Prefer our custom version(s) of this component",
        )
    }
}

Solution

  • The error message "This annotation is not applicable to target 'expression'" means that the @SuppressLint("PreferDefaultComposables") annotation cannot be used on an expression, only on a declaration (such as a class, field, method, or parameter).

    In your code, the @SuppressLint("PreferDefaultComposables") annotation is being used on the Button function call. This is not allowed, because the function call is an expression, not a declaration.

    To fix this there are two ways

    Approach 1 -

    @Composable
    fun ButtonText() {
    
        // Declare the Button function call inside a lambda expression
        @SuppressLint("PreferDefaultComposables")
        val button: Unit = Button(onClick = { }) {
            Text(text = "Click me")
        }
    }
    

    Approach 2

    @Composable
    fun ButtonText() {
    
        @SuppressLint("PreferDefaultComposables")
        @Composable
        fun InnerButton() {
            Button(onClick = { }) {
                Text(text = "Click me")
            }
        }
    
        InnerButton()
    }
    

    The default approach will declaring outside or onButtonText function. But your need is to declare inside. Hope these approach helps you