[Android]Custom Lint for Android x Kotlin

We can implement some custom lint for Kotlin code.
So, I put an example the custom lint.

Some helpful links.

You can also find examples in the GitHub repositories. Let’s try to implement custom lint and reduce easy mistake from your projects.

Gradle

dependencies {
    compileOnly "com.android.tools.lint:lint-api:26.0.1"
    compileOnly "com.android.tools.lint:lint-checks:26.0.1"
}

sourceCompatibility = "1.8"
targetCompatibility = "1.8"

jar {
    manifest {
        attributes("Lint-Registry-v2": "your.custom.lint.package.LintClassName")
    }
}

Detector

class yourCustomLintDetector : Detector(), UastScanner {
    companion object {
        private val issueId = "yourCustomLint"
        private val issueDescription = "Description for yourCustomLint"
        private val issueExplanation = "Some more concrete explanation for yourCustomLint"
        // https://android.googlesource.com/platform/tools/base/+/master/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Category.java#122
        private val issueCategory = Category.CORRECTNESS
        private val issuePriority = 10
        private val issueSeverity = Severity.FATAL
        private val implementation = Implementation(yourCustomLintDetector::class.java, Scope.JAVA_FILE_SCOPE)

        internal val issue = Issue.create(issueId, issueDescription, issueExplanation, issueCategory, issuePriority, issueSeverity, implementation)
    }

    override fun applicableSuperClasses() = listOf("some.super.class.of.yourCustomLint")

    // If a target, class which inherits "some.super.class.of.yourCustomLint", has no "yourCustomLintName" method, then the following lines report it user.
    override fun visitClass(context: JavaContext, declaration: UClass) {
        val isOverridden = declaration.methods
                .firstOrNull { method -> method.isOverride && method.name.contains("yourCustomLintName") } != null

        if (!isOverridden) {
            declaration.uastAnchor?.let {
                context.report(issue, context.getLocation(it), "$issueDescription \n $issueExplanation")
            }
        }
    }
}

Registry

import your.custom.lint.package

class LintClassName : IssueRegistry() {
    override fun getIssues() = listOf(yourCustomLintDetector.issue)
}

check

Add the lines on your gradle dependencies.

dependencies {
  lintChecks project(':your-lint-project')
}

Test

You can implement tests for the custom rule.

https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java

import com.android.tools.lint.checks.infrastructure.LintDetectorTest

class yourCustomLintDetectorNotOverrideTest : LintDetectorTest() {
    fun testBasic() {
        lint().files(
                LintDetectorTest.java("" +
                        "package test.pkg;\n" +
                        "import some.super.class.of.yourCustomLint;\n" +
                        "public class TestClass1 extends yourCustomLint {\n" +
                        "    @Override public void yourCustomLintName() {}\n" +
                        "}")) // an example
                .allowCompilationErrors()
                .run()
                .expect("Error message the lint should raise.....\n"
                        "1 errors, 0 warnings") // an example
    }

    override fun getDetector() = yourCustomLintDetector()

    override fun getIssues() = listOf(yourCustomLintDetector.issue)
}
import com.android.tools.lint.checks.infrastructure.LintDetectorTest

class yourCustomLintDetectorOverrideTest : LintDetectorTest() {
    fun testBasic() {
        lint().files(
                LintDetectorTest.java("" +
                        "package test.pkg;\n" +
                        "import some.super.class.of.yourCustomLint;\n" +
                        "public class TestClass1 extends yourCustomLint {\n" +
                        "    @Override public void yourCustomLintName() {}\n" +
                        "}")) // an example
                .allowCompilationErrors()
                .run()
                .expectClean()
    }

    override fun getDetector() = yourCustomLintDetector()

    override fun getIssues() = listOf(yourCustomLintDetector.issue)
}

Conclusion

I described an example to implement Custom Lint for Gradle plugin. I believe custom lint help your projects and if you work with some guys, the feature should help you more powerful.

Have a good testing πŸ™‚

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.