slack.lint.RedactedUsageDetector.kt Maven / Gradle / Ivy
The newest version!
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint
import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UField
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.kotlin.isKotlin
import slack.lint.util.sourceImplementation
/**
* A simple detector that ensures that `@Redacted` annotations are only used in Kotlin files (Java
* is unsupported).
*/
// TODO check for toString() impls in Kotlin classes using Redacted? i.e. it's an error to have both
// TODO check that redacted classes are data classes
class RedactedUsageDetector : Detector(), SourceCodeScanner {
override fun getApplicableUastTypes(): List> {
return listOf(UClass::class.java, UMethod::class.java, UField::class.java)
}
override fun createUastHandler(context: JavaContext): UElementHandler? {
// Redacted can only be used in Kotlin files, so this check only checks in Java files
if (isKotlin(context.uastFile?.lang)) return null
return object : UElementHandler() {
override fun visitClass(node: UClass) = node.checkRedacted()
override fun visitField(node: UField) = node.checkRedacted()
override fun visitMethod(node: UMethod) = node.checkRedacted()
fun UAnnotated.checkRedacted() {
uAnnotations
.find { it.qualifiedName?.contains(NAME_REDACTED, ignoreCase = true) == true }
?.let { redactedAnnotation ->
context.report(
JAVA_USAGE,
context.getLocation(redactedAnnotation),
JAVA_USAGE.getBriefDescription(TextFormat.TEXT),
quickfixData = null,
)
}
}
}
}
companion object {
// We check simple name only rather than any specific redacted annotation
private const val NAME_REDACTED = "redacted"
private val JAVA_USAGE: Issue =
Issue.create(
"RedactedInJavaUsage",
"@Redacted is only supported in Kotlin classes!",
"@Redacted is only supported in Kotlin classes!",
Category.CORRECTNESS,
9,
Severity.ERROR,
sourceImplementation(),
)
val ISSUES = listOf(JAVA_USAGE)
}
}