Я пытаюсь написать правило lint, чтобы поймать места, где результат функции RxJava2 никак не используется. Например:
final Observable<String> observable = getObservable();
observable.subscribe(this::onSuccess, this::onError);
В RxJava2 функция subscribe
возвращает Disposable
, который следует использовать для отмены подписки, если экземпляр программы/класса каким-то образом "заканчивается", чтобы предотвратить утечку памяти. Я хочу, чтобы моя сборка завершилась неудачей, если будут обнаружены подобные случаи.
Этот конкретный метод (и все остальные, которые меня интересуют) помечен 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());
}
Мой план состоит в том, чтобы написать пользовательское правило lint, которое:
- Ищет выражения, которые возвращают результат метода, аннотированного
io.reactivex.annotations.CheckReturnValue
. - Отфильтруйте результаты поиска, указав только те выражения, результат которых никогда не используется.
Например, вот некоторые случаи, которые не должны дать сбой:
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();
Мне удалось написать правило lint, которое находит экземпляры выражений вызова, результат которых помечен CheckReturnValue
, но я изо всех сил пытаюсь понять, как использовать API-интерфейсы JetBrains UAST/PSI, чтобы определить, используется ли результат. Это мое правило до сих пор:
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...
}
}
}
В настоящее время это не работает, поскольку сообщает обо всех случаях использования любой функции, помеченной CheckReturnValue
.
<issue id="CheckReturnValue" severity="error" />
в свой файлlint.xml
. - person marianosimone   schedule 10.04.2018