【问题标题】:Lint rule for number of injections in a constructor构造函数中注入次数的 Lint 规则
【发布时间】:2017-12-07 21:46:53
【问题描述】:

我正在尝试在我的 Android 代码中创建一个 Lint 规则,用于检查构造函数中的注入次数,因此,如果我超过某个视图模型的特定数量,例如,我将发出 lint 警告。

我知道我必须在我的 Lint Detector 中实现一个 UastScanner,但是我迷路了,因为我找不到好的文档。有没有其他人做过这样的事情?或者我在哪里可以找到关于它的好的文档?

谢谢!

【问题讨论】:

  • 我相信 lint 规则是 Android 中记录最少的东西之一。我发现最好有一个look at the existing ones 并从那里开始。

标签: android dependency-injection dagger-2 lint


【解决方案1】:

* 注意 - 阅读已编辑解决方案的完整答案。 *

我能够像这样编写 UAST 转换:

public class NumberOfDependenciesInjectedDetector extends Detector implements Detector.UastScanner {

    private static final int NUMBER_OF_INJECTIONS_ALLOWED = 5;
    private static final Class<? extends Detector> DETECTOR = NumberOfDependenciesInjectedDetector.class;
    private static final EnumSet<Scope> SCOPE = Scope.JAVA_FILE_SCOPE;
    private static final Implementation IMPLEMENTATION = new Implementation(DETECTOR, SCOPE);

    public static final Issue ISSUE = Issue.create(
            "NumberOfDependenciesInjected",
            "Limit number of injections in a class via constructor",
            "A longer description here",
            Category.CORRECTNESS,
            10,
            Severity.ERROR,
            IMPLEMENTATION
    );

    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UClass.class);
    }

    @Override
    public UElementHandler createUastHandler(JavaContext context) {
        return new ConstructorVisitor(context);
    }

    private static class ConstructorVisitor extends UElementHandler {

        private JavaContext javaContext;

        private ConstructorVisitor(JavaContext javaContext) {
            this.javaContext = javaContext;
        }

        @Override
        public void visitClass(UClass clazz){
            UMethod[] methods = clazz.getMethods();

            for(UMethod method : methods){
                if(!method.isConstructor()) continue;

                if (method.getParameterList().getParametersCount() > NUMBER_OF_INJECTIONS_ALLOWED) {
                    javaContext.report(ISSUE, method, javaContext.getLocation(method), "Injections exceed allowed value of " + NUMBER_OF_INJECTIONS_ALLOWED);
                }
            }
        }
    }
}

但是,这似乎仍然没有提取 Kotlin 源文件...非常混乱。

编辑:2017 年 12 月 19 日 - 已修复

问题有两个方面:

1) 确实存在阻止检查工作的 Psi 方法的隐藏用法。 visitClass 方法不应使用 getParameterList(),而应使用 getUastParameters()。将上面的访问类替换为:

@Override
public void visitClass(UClass clazz){
    UMethod[] methods = clazz.getMethods();

    for(UMethod method : methods){
        if(!method.isConstructor()) continue;

        if (method.getUastParameters().size() > NUMBER_OF_INJECTIONS_ALLOWED) {
            javaContext.report(ISSUE, clazz, javaContext.getLocation(method), "Injections exceed allowed value of " + NUMBER_OF_INJECTIONS_ALLOWED);
        }
    }
}

2) 在 lint-dev 小组直接与 Tor Norbye 交谈后,他指出 Android Studio 3.0 实际上 lint 不能在外部与 kotlin 一起工作,因此预计不会像这里描述的那样工作。升级到 Android Studio 3.1 Canary 并运行 linter 生成了预期的报告。

【讨论】:

  • 我怀疑问题出在 Scope.JAVA_FILE_SCOPE 上。但是,包括 Scope.ALL 在内的其他范围似乎都不起作用……除了 JAVA_FILE_SCOPE 之外,linter 不会报告有问题的文件。
  • 我能想到的唯一另一件事是在这个 sn-p 中有一些意外的 PSI 使用,但我肯定看不到在哪里。这样做的结果是只有 java 文件可以工作。
【解决方案2】:

所以我能够使用 JavaScanner 找到解决方案,但我还没有使用 UastScanner 找到任何解决方案(这是我想要使用的,因为我们也有 Kotlin 类):

public class NumberOfDependenciesInjectedDetector extends Detector implements Detector.JavaScanner {

    private static final int NUMBER_OF_INJECTIONS_ALLOWED = 5;
    private static final Class<? extends Detector> DETECTOR = NumberOfDependenciesInjectedDetector.class;
    private static final EnumSet<Scope> SCOPE = Scope.JAVA_FILE_SCOPE;
    private static final Implementation IMPLEMENTATION = new Implementation(DETECTOR, SCOPE);

    public static final Issue ISSUE = Issue.create(
            "NumberOfDependenciesInjected",
            "Limit number of injections in a class via constructor",
            "A longer description here",
            Category.CORRECTNESS,
            10,
            Severity.ERROR,
            IMPLEMENTATION
    );

    @Override
    public boolean appliesTo(Context context, File file) {
        return true;
    }

    @Override
    public Speed getSpeed(Issue issue) {
        return Speed.FAST;
    }

    @Override
    public List<Class<? extends Node>> getApplicableNodeTypes() {
        return Collections.<Class<? extends Node>>singletonList(ConstructorDeclaration.class);
    }

    @Override
    public AstVisitor createJavaVisitor(JavaContext context) {
        return new ConstructorVisitor(context);
    }

    private static class ConstructorVisitor extends ForwardingAstVisitor {

        private JavaContext javaContext;

        private ConstructorVisitor(JavaContext javaContext) {
            this.javaContext = javaContext;
        }

        @Override
        public boolean visitConstructorDeclaration(ConstructorDeclaration node) {
            if (node.astParameters().size() > NUMBER_OF_INJECTIONS_ALLOWED) {
                javaContext.report(ISSUE, node, javaContext.getLocation(node), "My message goes here");
                return true;
            }
            return false;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多