0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

前置き

KotlinのListでget()を使用した場合、IndexOutOfBoundsExceptionがスローされる可能性があります。
そのため、使用された場合に警告を表示するカスタムLintを作ることにしました。

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

要件

今回は下記に対して警告を表示します。

  • Listの get() メソッドのみ
    • Map#get()やカスタムゲッターには警告を表示しない
  • 演算子オーバーロードにも対応
    • 例) listof("hoge")[0]

実装

環境

app/build.gradle
compileOnly("com.android.tools.lint:lint-api:31.2.2")

コード解析には SourceCodeScanner を用います。

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

上記のコードは下記を参考にさせていただきました。

あとがき

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?