前置き
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
でないといけない点はご注意ください。
上記のコードは下記を参考にさせていただきました。
あとがき
メソッドを呼び出している型名を確認して警告を表示することができました。
getOrNull()へのQuickFixを提供するなど、拡張の余地はありそうです。
この記事がどなたかの参考になると幸いです。