确保不调用特定方法的自定义棉绒规则

时间:2018-08-18 15:07:00

标签: java android kotlin lint

我想确保我的Android应用程序(Java和Kotlin代码)均未调用特定类的特定方法。假设我有一个名为Bar的类,它使用两种方法:allowed()disallowed()。这是代码:

package com;

public class Bar {

    public void disallowed() {
    }

    public void allowed() {
    }
}

并且假设客户端代码可以调用allowed(),而不应该调用disallowed()。我发现内置AddJavascriptInterfaceDetector的Google源代码与我的案例类似。

这是我的棉绒规则的代码:

SampleCodeDetector

package com.sample.mobile.lint

import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression


class SampleCodeDetector : Detector(), SourceCodeScanner {

    companion object {
        @JvmField
        val ISSUE = Issue.create(
                // ID: used in @SuppressLint warnings etc
                "Usage of Bar#disallowed()",

                // Title -- shown in the IDE's preference dialog, as category headers in the
                // Analysis results window, etc
                "Usage of Bar#disallowed() - Summary",

                // Full explanation of the issue; you can use some markdown markup such as
                // `monospace`, *italic*, and **bold**.
                "This check highlights the usage of Bar#disallowed()",
                Category.CORRECTNESS,
                8,
                Severity.ERROR,
                Implementation(
                        SampleCodeDetector::class.java,
                        Scope.JAVA_FILE_SCOPE
                )
        )

        const val FULLY_QUALIFIED_CLASS_NAME = "com.Bar"
        const val METHOD_NAME = "disallowed"
    }

    override fun getApplicableMethodNames() = listOf(METHOD_NAME)

    override fun visitMethod(context: JavaContext, node: UCallExpression, method: PsiMethod) {
        val evaluator = context.evaluator
        if (!evaluator.methodMatches(method, FULLY_QUALIFIED_CLASS_NAME, true)) {
            return
        }

        val message = "`Bar.disallowed()` should not be called"
        context.report(ISSUE, node, context.getNameLocation(node), message)

    }
}

SampleIssueRegistry

package com.sample.mobile.lint

import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.Issue

class SampleIssueRegistry : IssueRegistry() {

    override val issues: List<Issue> get() = listOf(SampleCodeDetector.ISSUE)
}

build.gradle

apply plugin: 'java-library'

dependencies {

    String lintVersion = "26.1.1"

    compileOnly "com.android.tools.lint:lint-api:$lintVersion"
    compileOnly "com.android.tools.lint:lint-checks:$lintVersion"
    compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.60"

    testImplementation "com.android.tools.lint:lint:$lintVersion"
    testImplementation "com.android.tools.lint:lint-tests:$lintVersion"


}

sourceCompatibility = "1.8"
targetCompatibility = "1.8"

jar {
    manifest {
        // Only use the "-v2" key here if your checks have been updated to the
        // new 3.0 APIs (including UAST)
        attributes("Lint-Registry-v2": "com.sample.mobile.lint.SampleIssueRegistry")
    }
}

SampleCodeDetectorTest

package com.sample.mobile.lint

import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.detector.api.Detector

class SampleCodeDetectorTest : LintDetectorTest() {

    private val javaFile = "package com.sample.mobile.app;\n" +
            "\n" +
            "import android.util.Log;\n" +
            "\n" +
            "import com.Bar;\n" +
            "\n" +
            "public class Foo {\n" +
            "\n" +
            "    public void calLLog() {\n" +
            "        int a = 2;\n" +
            "        int b = 8;\n" +
            "        Log.d(\"Tag\", \"a+b=\" + (a + b));\n" +
            "        \n" +
            "        Bar bar = new Bar();\n" +
            "        bar.allowed();\n" +
            "        bar.disallowed();\n" +
            "    }\n" +
            "}\n"

    fun testJava() {
        lint().files(LintDetectorTest.java(javaFile))
                .run()
                .expect("")
    }

    override fun getDetector(): Detector? {
        return SampleCodeDetector()
    }

    override fun getIssues() = listOf(SampleCodeDetector.ISSUE)
}

不幸的是,测试失败:

org.junit.ComparisonFailure: 
Expected :
Actual   :No warnings.

当然,期望的结果不是一个空字符串,而是“没有警告”。错误,因为在Bar#disallowed()中有对class Foo的呼叫

1 个答案:

答案 0 :(得分:0)

您还应该在测试中包含com.Bar类的源代码:

import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.detector.api.Detector

class SampleCodeDetectorTest : LintDetectorTest() {

    private val javaFile = """
        package com.sample.mobile.app;

        import android.util.Log;

        import com.Bar;

        public class Foo {

            public void calLLog() {
                int a = 2;
                int b = 8;
                Log.d("Tag", "a+b=" + (a + b));

                Bar bar = new Bar();
                bar.allowed();
                bar.disallowed();
            }
        }
    """.trimIndent()

    private val barJavaFile = """
        package com;

        public class Bar {

            public void disallowed() {
            }

            public void allowed() {
            }
        }
    """.trimIndent()

    fun testJava() {
        lint().files(java(javaFile), java(barJavaFile))
                .run()
                .expect("""
                    src/com/sample/mobile/app/Foo.java:16: Error: Bar.disallowed() should not be called [Usage of Bar#disallowed()]
                            bar.disallowed();
                                ~~~~~~~~~~
                    1 errors, 0 warnings
                """.trimIndent())
    }

    override fun getDetector(): Detector? {
        return SampleCodeDetector()
    }

    override fun getIssues() = listOf(SampleCodeDetector.ISSUE)
}

棉绒只是不知道com.Bar是什么,而默默地忽略了它。