how to write Android Custom lint rule to forbid calling specific function in all classes that extends specific type?

Viewed 65

I want to write a custom lint rule to ban calling function states.accept() in all classes that extends BaseViewModel where states is a BehaviorRelay object.

how can I achieve something like this. I’ve written the check using visitMethodCall but this only can check the function name and if it’s member of BehaviorRelay,

the missing part is how to check if this function is being called in children’s of BaseViewModel.

below is the part that works: using visitMethodCall but detecting the function in whole code.

override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
    val evaluator = context.evaluator
    if (evaluator.isMemberInClass(method, BEHAVIOR_RELAY)) {
        if (method.name == ACCEPT_FUNCTION) {
            context.report(
                Incident(
                    issue = ISSUE,
                    scope = node,
                    location = context.getNameLocation(node),
                    message = "View Models implements `BaseViewModel` must not update `states`"
                )
            )
        }
    }
}
1 Answers

applicableSuperClasses will filter only the classes that extends passed types, in my case BaseViewModel. this function works with visitClass.

Then using AbstractUastVisitor() to visit all calls in that class and find the specific function by name, also checking if it's a member function in target type.

full working code.

 override fun applicableSuperClasses(): List<String>? {
        return listOf(BASE_VIEW_MODEL)
    }

    override fun visitClass(context: JavaContext, declaration: UClass) {
        val evaluator = context.evaluator

        declaration.accept(object : AbstractUastVisitor() {
            override fun visitCallExpression(node: UCallExpression): Boolean {

                val isRelayFunction = evaluator.isMemberInClass(
                    node.resolve(),
                    BEHAVIOR_RELAY
                )
                if (node.methodName == ACCEPT_FUNCTION && isRelayFunction) {
                    context.report(
                        issue = ISSUE,
                        scope = node,
                        location = context.getNameLocation(node),
                        message = "View Models implements `BaseViewModel` must not update `states`"
                    )
                }
                return super.visitCallExpression(node)
            }
        })
    }
Related