确保特定方法未被调用的自定义lint规则

10

我希望确保在我的Android应用程序中(包括Java和Kotlin代码),不会调用特定类的特定方法。假设我有一个名为Bar的类,其中包括两个方法:allowed()disallowed()。以下是代码:

package com;

public class Bar {

    public void disallowed() {
    }

    public void allowed() {
    }
}

假设客户端代码可以调用allowed(),但不应该调用disallowed()。我找到了谷歌内置AddJavascriptInterfaceDetector的源代码,它与我的情况类似。

以下是我的lint规则代码:

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)

    }
}

样例问题注册表

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.

当然,期望的结果不是空字符串,但无论如何,“No warnings.” 都是不正确的,因为在 class Foo 中调用了 Bar#disallowed()

我知道这并没有回答问题,但在我看来,最简单的解决方案是将该方法不设为“public”,而是设置为其他可见性,并以这种方式进行封装。 - leonardkraemer
@MaksimDmitriev 只是确保这不是XY问题的情况 ;)。 - leonardkraemer
1
@Roland 对象和字符串是 addJavascriptInterface 的参数。disallowed() 没有任何参数。 - Maksim Dmitriev
@MaksimDmitriev 如果您想避免暴露特定的方法,可以将涉及该方法的类包装在另一个类中。 - Matteo Ugolotti
请查看@RestrictTo注解。 - ucMedia
显示剩余2条评论
1个回答

2

您还应该将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)
}

Lint不知道com.Bar是什么,因此默默地忽略它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接