
slack.lint.resources.WrongResourceImportAliasDetector.kt Maven / Gradle / Ivy
// Copyright (C) 2022 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.resources
import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Context
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.LintFix
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UImportStatement
import org.jetbrains.uast.USimpleNameReferenceExpression
import slack.lint.resources.ImportAliasesLoader.IMPORT_ALIASES
import slack.lint.resources.model.RootIssueData
import slack.lint.util.sourceImplementation
/** Reports an error when an R class is imported using the wrong import alias. */
class WrongResourceImportAliasDetector : Detector(), SourceCodeScanner {
private val fixes = mutableListOf()
private var rootIssueData: RootIssueData? = null
private lateinit var importAliases: Map
override fun beforeCheckRootProject(context: Context) {
super.beforeCheckRootProject(context)
importAliases = ImportAliasesLoader.loadImportAliases(context)
}
override fun afterCheckFile(context: Context) {
// Collect all the fixes and apply them to one issue on the import to avoid renaming the import
// alias with a fix
// and leaving the R references still referencing the old import alias or vice versa.
rootIssueData?.let {
context.report(
ISSUE,
it.nameLocation,
"Use ${it.alias} as an import alias here",
quickfixData =
fix()
.name("Replace import alias")
// Apply the fixes in reverse so that the ranges/locations don't change.
.composite(*fixes.reversed().toTypedArray())
.autoFix(),
)
reset()
}
}
private fun reset() {
rootIssueData = null
fixes.clear()
}
override fun getApplicableUastTypes(): List> =
listOf(UImportStatement::class.java, USimpleNameReferenceExpression::class.java)
override fun createUastHandler(context: JavaContext): UElementHandler {
return object : UElementHandler() {
private var wrongAlias: String? = null
override fun visitImportStatement(node: UImportStatement) {
// Import alias is a Kotlin feature.
val importDirective = node.sourcePsi as? KtImportDirective ?: return
// In case of multiple wrong aliases, only fix the first one.
if (wrongAlias != null) return
val importedFqNameString = importDirective.importedFqName?.asString() ?: return
if (importedFqNameString.endsWith(".R") && importDirective.aliasName != null) {
importAliases[importedFqNameString]?.let { alias ->
val aliasName = importDirective.aliasName
if (alias != aliasName) {
this.wrongAlias = aliasName
[email protected] =
RootIssueData(
alias = alias,
nameLocation = context.getNameLocation(importDirective),
)
fixes.add(createImportLintFix(node, importedFqNameString, aliasName, alias))
}
}
}
}
private fun createImportLintFix(
node: UImportStatement,
importedFqNameString: String,
aliasName: String?,
alias: String,
): LintFix {
return fix()
.replace()
.range(context.getLocation(node))
.text("$importedFqNameString as $aliasName")
.with("$importedFqNameString as $alias")
.build()
}
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression) {
wrongAlias?.let {
if (node.asSourceString() == it) {
fixes.add(createReferenceLintFix(node))
}
}
}
private fun createReferenceLintFix(node: USimpleNameReferenceExpression): LintFix {
return fix().replace().range(context.getLocation(node)).with(rootIssueData?.alias).build()
}
}
}
companion object {
val ISSUE: Issue =
Issue.create(
"WrongResourceImportAlias",
"Wrong import alias for this R class.",
"R class import aliases should be consistent across the codebase. For example: \n" +
"import slack.l10n.R as L10nR\n" +
"import slack.uikit.R as UiKitR",
Category.CORRECTNESS,
6,
Severity.ERROR,
sourceImplementation(),
)
.setOptions(listOf(IMPORT_ALIASES))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy