自定义 lint 检查:在表达式调用中识别泛型
Custom lint check: identify generic on expression call
我正在尝试编写一个 lint 检查,它访问调用表达式,并报告一些基于泛型的违规行为。
为了更清楚,假设我有这个代码:
object Foo {
inline fun <reified T> bar() = T::class.java
}
假设我想编写一个 lint 检查,当我使用 Int
作为通用类型调用 bar
方法时会发出抱怨,但接受其他所有内容。
因此,使用以下代码,第二次调用 bar 应该会触发警告:
object Whatever {
fun someMethod() {
val stringClass = Foo.bar<String>() // Should not complain
val intClass = Foo.bar<Int>() // Should raise a warning
}
}
如何实施?这当然不是真正的用例,我真正想做的是正确检测 bar<Int>
.
到目前为止,这是我拥有的:
class MyDetector : Detector(), SourceCodeScanner {
companion object Issues {
val ISSUE = Issue.create(
id = "IntBarTest",
briefDescription = "You used bar with an Int type!",
explanation = "Explanation",
category = Category.CORRECTNESS,
severity = FATAL,
implementation = Implementation(MyDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
}
override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
override fun createUastHandler(context: JavaContext): UElementHandler {
return GenericTypeHandler(context)
}
inner class GenericTypeHandler(val context: JavaContext) : UElementHandler() {
override fun visitCallExpression(node: UCallExpression) {
if (isCallExpressionAnIntBar(node)) {
context.report(ISSUE,
context.getNameLocation(node),
"Oh no, I should not use bar<Int>")
}
}
private fun isCallExpressionAnIntBar(node: UCallExpression): Boolean {
return if ("bar".equals(node.methodName) ||
"Foo" == (node.receiverType as? PsiClassReferenceType)?.resolve()?.qualifiedName) {
// We know it's the method we are looking for but now we must identify the generic
TODO("Identify the generic")
} else {
false
}
}
}
}
如您所见,有一个很大的 TODO :-P
其实答案很简单:
UCallExpression 公开类型参数。因此,您只需执行以下操作:
private fun isCallExpressionAnIntBar(node: UCallExpression): Boolean {
return if ("bar".equals(node.methodName) ||
"Foo" == (node.receiverType as? PsiClassReferenceType)?.resolve()?.qualifiedName) {
// We know it's the method we are looking for but now we must identify the generic
node.typeArguments.getOrNull(0)?.canonicalText == Int::class.qualifiedName
} else {
false
}
}
它让我望而却步的原因是我正在开发测试和检测器中的断点。但是我的测试代码是错误的(没有正确定义依赖关系等等),所以检测器当然不能正常工作:我的对象的类型参数是一个空数组。
修复测试设置后,typeArguments
正确报告了通用类型。
我正在尝试编写一个 lint 检查,它访问调用表达式,并报告一些基于泛型的违规行为。
为了更清楚,假设我有这个代码:
object Foo {
inline fun <reified T> bar() = T::class.java
}
假设我想编写一个 lint 检查,当我使用 Int
作为通用类型调用 bar
方法时会发出抱怨,但接受其他所有内容。
因此,使用以下代码,第二次调用 bar 应该会触发警告:
object Whatever {
fun someMethod() {
val stringClass = Foo.bar<String>() // Should not complain
val intClass = Foo.bar<Int>() // Should raise a warning
}
}
如何实施?这当然不是真正的用例,我真正想做的是正确检测 bar<Int>
.
到目前为止,这是我拥有的:
class MyDetector : Detector(), SourceCodeScanner {
companion object Issues {
val ISSUE = Issue.create(
id = "IntBarTest",
briefDescription = "You used bar with an Int type!",
explanation = "Explanation",
category = Category.CORRECTNESS,
severity = FATAL,
implementation = Implementation(MyDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
}
override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
override fun createUastHandler(context: JavaContext): UElementHandler {
return GenericTypeHandler(context)
}
inner class GenericTypeHandler(val context: JavaContext) : UElementHandler() {
override fun visitCallExpression(node: UCallExpression) {
if (isCallExpressionAnIntBar(node)) {
context.report(ISSUE,
context.getNameLocation(node),
"Oh no, I should not use bar<Int>")
}
}
private fun isCallExpressionAnIntBar(node: UCallExpression): Boolean {
return if ("bar".equals(node.methodName) ||
"Foo" == (node.receiverType as? PsiClassReferenceType)?.resolve()?.qualifiedName) {
// We know it's the method we are looking for but now we must identify the generic
TODO("Identify the generic")
} else {
false
}
}
}
}
如您所见,有一个很大的 TODO :-P
其实答案很简单:
UCallExpression 公开类型参数。因此,您只需执行以下操作:
private fun isCallExpressionAnIntBar(node: UCallExpression): Boolean {
return if ("bar".equals(node.methodName) ||
"Foo" == (node.receiverType as? PsiClassReferenceType)?.resolve()?.qualifiedName) {
// We know it's the method we are looking for but now we must identify the generic
node.typeArguments.getOrNull(0)?.canonicalText == Int::class.qualifiedName
} else {
false
}
}
它让我望而却步的原因是我正在开发测试和检测器中的断点。但是我的测试代码是错误的(没有正确定义依赖关系等等),所以检测器当然不能正常工作:我的对象的类型参数是一个空数组。
修复测试设置后,typeArguments
正确报告了通用类型。