我正在尝试编写我的第一个 Lint 规则。现在我只想检测注释的使用@AnyThread。
我创建了一个模块来实现我的自定义规则。该模块的 gradle 文件是(我使用 gradle 插件版本 3.6.1):
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
compileOnly 'com.android.tools.lint:lint-api:26.6.1'
compileOnly 'com.android.tools.lint:lint-checks:26.6.1'
testImplementation "com.android.tools.lint:lint:26.6.1"
testImplementation "com.android.tools.lint:lint-tests:26.6.1"
testImplementation "com.android.tools:testutils:26.6.1"
testImplementation "junit:junit:4.12"
}
jar {
manifest {
attributes("Lint-Registry-v2": "com.test.lint.MyIssueRegistry")
}
}
我的探测器是:
package com.test.lint
//...
class AnyThreadAnnotationDetector: AbstractAnnotationDetector(), Detector.UastScanner {
companion object {
private const val AnyThreadId = "AnyThreadId"
const val AnyThreadDescription = "This is an attempt to find AnyThread annotation in code"
const val AnyThreadExplanation = "AnyThread annotation found!"
val ANYTHREAD_ANNOTATION_ISSUE = Issue.create(
id = AnyThreadId,
briefDescription = AnyThreadDescription,
explanation = AnyThreadExplanation,
category = Category.CORRECTNESS,
priority = 4,
severity = Severity.INFORMATIONAL,
implementation = Implementation(
AnyThreadAnnotationDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
}
override fun applicableAnnotations(): List<String>? = listOf("androidx.annotation.AnyThread")
override fun visitAnnotationUsage(
context: JavaContext,
usage: UElement,
type: AnnotationUsageType,
annotation: UAnnotation,
qualifiedName: String,
method: PsiMethod?,
annotations: List<UAnnotation>,
allMemberAnnotations: List<UAnnotation>,
allClassAnnotations: List<UAnnotation>,
allPackageAnnotations: List<UAnnotation>
) {
context.report(
issue = ANYTHREAD_ANNOTATION_ISSUE,
scope = usage,
location = context.getNameLocation(usage),
message = "A message"
)
}
}
我IssueRegistry的是:
class MyIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
get() = listOf(
AnyThreadAnnotationDetector.ANYTHREAD_ANNOTATION_ISSUE)
override val api: Int = CURRENT_API
}
我写了一些测试:
class AnyThreadAnnotationDetectorTest {
@Test
fun noAnnotatedFileKotlin() {
TestLintTask.lint()
.files(
LintDetectorTest.kotlin(
"""
|package foo;
|
|class XmlHttpRequest {
|}""".trimMargin()
)
)
.allowMissingSdk()
.issues(AnyThreadAnnotationDetector.ANYTHREAD_ANNOTATION_ISSUE)
.run()
.expectClean()
}
@Test
fun annotatedKotlinMethod() {
TestLintTask.lint()
.files(
LintDetectorTest.kotlin(
"""
|package foo;
|
|import androidx.annotation.AnyThread
|
|class XmlHttpRequest {
|@AnyThread
|fun test(){}
|}""".trimMargin()
)
)
.allowMissingSdk()
.issues(AnyThreadAnnotationDetector.ANYTHREAD_ANNOTATION_ISSUE)
.run()
.expect(
"""
Just a test to find annotations
0 errors, 0 warnings
""".trimIndent()
)
}
@Test
fun testNoisyDetector() {
TestLintTask.lint().files(Stubs.ANYTHREAD_EXPERIMENT)
.allowMissingSdk()
.issues(AnyThreadAnnotationDetector.ANYTHREAD_ANNOTATION_ISSUE)
.run()
.expect(
"""
Just a test to find annotations
0 errors, 0 warnings
""".trimIndent()
)
}
}
在哪里Stubs.ANYTHREAD_EXPERIMENT:
object Stubs {
val ANYTHREAD_EXPERIMENT = kotlin(
"com/test/applicationlintdemoapp/AnythreadAnnotationStubs.kt",
"""
package com.test.applicationlintdemoapp
import androidx.annotation.AnyThread
class AnythreadClassExperiment {
@AnyThread
fun setTimeToNow() {
TimeTravelProvider().setTime(System.currentTimeMillis())
}
@AnyThread
fun setTimeToEpoch() {
TimeTravelProvider().setTime(0)
}
fun violateTimeTravelAccords() {
TimeTravelProvider().setTime(-1)
}
}
"""
).indented().within("src")
}
我所有的测试都失败了(除了noAnnotatedFileKotlin),实际上如果我在调试模式下对测试的调用设置断点就context.report永远不会暂停,这意味着androidx.annotation.AnyThread永远不会检测到注释。
会出什么问题?我错过了什么?
我看过并阅读了一些文档:
- 编写自定义 lint 规则
- KotlinConf 2017 - 使用 Android Lint 进行 Kotlin 静态分析
- 编写你的第一个 Lint 检查
- 为 Kotlin 代码制作自定义 Lint
- 充分利用 Android Lint
- 风格编码:使用自定义 Lint 规则进行静态分析
我通过实现NoisyDetector谈话编码风格中给出的控制配置:使用自定义 Lint 规则进行静态分析,测试结果很好。