简体   繁体   中英

How to check if the result of an expression is used in a custom Android lint rule

I'm trying to write a lint rule to catch places where the result of an RxJava2 function is not used in anyway. For example:

final Observable<String> observable = getObservable();
observable.subscribe(this::onSuccess, this::onError);

In RxJava2, the subscribe function returns a Disposable that should be used to unsubscribe if the program/class instance "finishes" in some way in order to prevent memory leaks. I want to fail my build if any occurences like this are found.

This particular method (and all of the other ones I'm interested in) is annotated with io.reactivex.annotations.CheckReturnValue :

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {
    return subscribe(onNext, onError, Functions.EMPTY_ACTION, Functions.emptyConsumer());
}

My plan is to write a custom lint rule that:

  • Searches for expressions that return the result of a method annotated with io.reactivex.annotations.CheckReturnValue
  • Filter the searches down to only expressions whose result is never used

For example, here are some cases that should not fail:

final CompositeDisposable compositeDisposable = new CompositeDisposable();
// Result of subscribe passed into another function
compositeDisposable.add(observable.subscribe(this::onSuccess, this::onError).dispose());

// Result of subscribe stored in a variable
final Disposable disposable = observable.subscribe(this::onSuccess, this::onError);

// Result of subscribe used
observable.subscribe(this::onSuccess, this::onError).dispose();

I've managed to write a lint rule that finds instances of call expressions where the result is annotated with CheckReturnValue , but I'm struggling to figure out how to use the JetBrains UAST/PSI APIs to work out if the result is used. This is my rule so far:

class RxJava2CheckReturnValueMethodNotAssigned : Detector(), Detector.UastScanner {
    override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

    override fun createUastHandler(context: JavaContext) = CheckReturnValueVisitor(context)

    class CheckReturnValueVisitor(private val context: JavaContext) : UElementHandler() {

        override fun visitCallExpression(node: UCallExpression) {
            val method = node.resolve() ?: return
            if (!isCheckReturnValueAnnotatedMethod(method)) {
                return
            }
            if (!isResultOfCallUsed(node)) {
                return
            }

            reportIssue(node)
        }

        private fun isCheckReturnValueAnnotatedMethod(method: PsiMethod): Boolean {
            return context.evaluator.getAllAnnotations(method, true)
                .any { "io.reactivex.annotations.CheckReturnValue" == it.qualifiedName }
        }

        private fun isResultOfCallUsed(node: UCallExpression): Boolean {
            // Need to check is the result of the expression is used in some way
            return false
        }

        private fun reportIssue(node: UCallExpression) {
            // SNIP...
        }
    }
}

This currently doesn't work because it reports all usages of any function annotated with CheckReturnValue .

据我所知, node.resolve()经常返回 null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM