【Android】List#getを使用している場合に警告を出す

## 前置き
KotlinのListで[get()](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/get.html)を使用した場合、[IndexOutOfBoundsException](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-index-out-of-bounds-exception/)がスローされる可能性があります。
そのため、使用された場合に警告を表示するカスタムLintを作ることにしました。

なお、こちらの記事ではカスタムLintの0からの作成方法は記載しませんので、ご留意ください。

## 要件
今回は下記に対して警告を表示します。
* Listの `get()` メソッドのみ
  * [Map#get()](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-map/get.html)やカスタムゲッターには警告を表示しない
* 演算子オーバーロードにも対応
  * 例） `listof("hoge")[0]`

## 実装
環境
``` app/build.gradle
compileOnly("com.android.tools.lint:lint-api:31.2.2")
```

コード解析には [SourceCodeScanner](https://android.googlesource.com/platform/tools/base/+/mirror-goog-studio-main/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/SourceCodeScanner.kt) を用います。

``` ListGetDetector.kt
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression

class ListGetDetector : Detector(), SourceCodeScanner {
    override fun getApplicableMethodNames(): List<String> {
        return listOf("get")
    }

    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
        super.visitMethodCall(context, node, method)

        if (context.evaluator.isMemberInClass(method, "java.util.List")) {
            context.report(
                LIST_GET_ISSUE,
                node,
                context.getLocation(node),
                "Be careful IndexOutOfBoundsException",
            )
        }
    }

    companion object {
        val LIST_GET_ISSUE =
            Issue.create(
                id = "LIST_GET_ISSUE",
                briefDescription = "List#get may cause IndexOutOfBoundsException",
                explanation =
                """
                References to indexes outside the range of the list may cause an IndexOutOfBoundsException.
                Check the index range or consider using getOrNull().
                """,
                severity = Severity.WARNING,
                implementation = Implementation(ListGetDetector::class.java, Scope.JAVA_FILE_SCOPE),
            )
    }
}
```


`applicatableMethodName()` に "get" と指定することで、メソッド名が「get」に該当したPsiMethodが、演算子オーバーロード含め、`visitMethodCall()` で呼び出されます。
そして `JavaEvaluator#inMemberClass` でListかどうかを確認します。このとき型名は完全修飾子を指定しますが、 `kotlin.collections.List` ではなく `java.util.List` でないといけない点はご注意ください。

結果、以下のように警告が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610553/00e62eaf-d720-5e7f-ed3e-765ee8b6ffac.png)

上記のコードは下記を参考にさせていただきました。
* https://github.com/googlesamples/android-custom-lint-rules/blob/main/checks/src/main/java/com/example/lint/checks/AvoidDateDetector.kt

## あとがき
メソッドを呼び出している型名を確認して警告を表示することができました。
getOrNull()へのQuickFixを提供するなど、拡張の余地はありそうです。

この記事がどなたかの参考になると幸いです。