编写自定义lint警告以检查自定义注释

Mos*_*ses 6 android android-lint android-studio java-annotations

我写了以下注释:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
public @interface Warning {

}
Run Code Online (Sandbox Code Playgroud)

这是为了注释那些在不小心调用时可能导致问题的方法.我在我的项目中添加了一个注释处理器,但这只在javac命令的日志输出中提供了警告.我希望此警告与其他lint警告一起出现在Android Studio中,并且调用带有此批注的方法.这就是我尝试编写自定义lint规则的原因.我有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.Scope;
import com.android.tools.lint.detector.api.Severity;

public class CaimitoDetector extends Detector implements Detector.JavaScanner {

  public static final Issue ISSUE = Issue.create(
      "WarningAnnotation",
      "This method has been annotated with @Warning",
      "This method has special conditions surrounding it's use, be careful when using it and refer to its documentation.",
      Category.USABILITY, 7, Severity.WARNING,
      new Implementation(CaimitoDetector.class, Scope.JAVA_FILE_SCOPE));

  @Override
  public void visitMethod(JavaContext context, AstVisitor visitor, MethodInvocation node) {

  }

}
Run Code Online (Sandbox Code Playgroud)
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;

import java.util.Collections;
import java.util.List;

public class CaimitoIssueRegistry extends IssueRegistry {

  @Override
  public List<Issue> getIssues() {
    return Collections.singletonList(CaimitoDetector.ISSUE);
  }

}
Run Code Online (Sandbox Code Playgroud)

但我不知道如何从这里开始.如何检查方法上是否存在annoation,并发出警告,使其在Android Studio中可见?

UPDATE

这是我的Detector类,适合任何想要做同样事情的人:

import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
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.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.Speed;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import lombok.ast.AstVisitor;
import lombok.ast.ConstructorInvocation;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;

public class CaimitoAnnotationDetector extends Detector implements Detector.JavaScanner {

  private static final String WARNING_ANNOTATION = "com.treemetrics.caimito.annotations.Warning";

  public static final Issue ISSUE = Issue.create(
      "Waqrning.",
      "Be careful when using this method.",
      "This method has special conditions surrounding it's use," +
          " be careful when calling it and refer to its documentation.",
      Category.USABILITY,
      7,
      Severity.WARNING,
      new Implementation(
          CaimitoAnnotationDetector.class,
          Scope.JAVA_FILE_SCOPE));

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

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

  private static void checkMethodAnnotation(@NonNull JavaContext context,
                                            @NonNull ResolvedMethod method,
                                            @NonNull Node node,
                                            @NonNull ResolvedAnnotation annotation) {
    String signature = annotation.getSignature();
    if(WARNING_ANNOTATION.equals(signature) || signature.endsWith(".Warning")) {
      checkWarning(context, node, annotation);
    }
  }

  private static void checkWarning(@NonNull JavaContext context,
                                      @NonNull Node node,
                                      @NonNull ResolvedAnnotation annotation) {
    context.report(ISSUE, node, context.getLocation(node), "Warning");
  }

  // ---- Implements JavaScanner ----

  @Override
  public List<Class<? extends Node>> getApplicableNodeTypes() {
    return Arrays.asList(
        MethodInvocation.class,
        ConstructorInvocation.class);
  }

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

  private static class CallChecker extends ForwardingAstVisitor {

    private final JavaContext mContext;

    public CallChecker(JavaContext context) {
      mContext = context;
    }

    @Override
    public boolean visitMethodInvocation(@NonNull MethodInvocation call) {
      ResolvedNode resolved = mContext.resolve(call);
      if(resolved instanceof ResolvedMethod) {
        ResolvedMethod method = (ResolvedMethod) resolved;
        checkCall(call, method);
      }

      return false;
    }

    @Override
    public boolean visitConstructorInvocation(@NonNull ConstructorInvocation call) {
      ResolvedNode resolved = mContext.resolve(call);
      if(resolved instanceof ResolvedMethod) {
        ResolvedMethod method = (ResolvedMethod) resolved;
        checkCall(call, method);
      }

      return false;
    }

    private void checkCall(@NonNull Node call, ResolvedMethod method) {
      Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
      annotations = filterRelevantAnnotations(annotations);
      for(ResolvedAnnotation annotation : annotations) {
        checkMethodAnnotation(mContext, method, call, annotation);
      }
    }

    private Iterable<ResolvedAnnotation> filterRelevantAnnotations(Iterable<ResolvedAnnotation> resolvedAnnotationsIn) {
      List<ResolvedAnnotation> resolvedAnnotationsOut = new ArrayList<>();
      for(ResolvedAnnotation resolvedAnnotation : resolvedAnnotationsIn) {
        if(resolvedAnnotation.matches(WARNING_ANNOTATION)) {
          resolvedAnnotationsOut.add(resolvedAnnotation);
        }
      }

      return resolvedAnnotationsOut;
    }

  }

}
Run Code Online (Sandbox Code Playgroud)

更新2

您可以通过在项目的根目录中创建lint.xml文件并在其中添加自定义lint规则来将自定义lint检查与Android Studio检查集成,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <issue id="Warning" severity="warning"/>
</lint>
Run Code Online (Sandbox Code Playgroud)

请注意,问题标记的id是CaimitoDetector类中Issue.create()方法的第一个参数提供的id.您还必须将通过构建lint规则输出的jar文件复制到/home/{user}/.android/lint文件夹中以使其正常工作.我为此写了一个自定义gradle任务.这是我的lint规则的build.gradle文件

apply plugin: 'java'

targetCompatibility = '1.7'
sourceCompatibility = '1.7'

repositories {
    jcenter()
}

dependencies {
    compile 'com.android.tools.lint:lint-api:24.2.1'
    compile 'com.android.tools.lint:lint-checks:24.2.1'
}

jar {
    manifest {
        attributes 'Manifest-Version': 1.0
        attributes 'Lint-Registry': 'com.treemetrics.caimito.lint.CaimitoIssueRegistry'
    }
}

defaultTasks 'assemble'

task copyLintJar(type: Copy) {
    description = 'Copies the caimito-lint jar file into the {user.home}/.android/lint folder.'
    from('build/libs/')
    into(System.getProperty("user.home") + '/.android/lint')
    include("*.jar")
}

// Runs the copyLintJar task after build has completed.
build.finalizedBy(copyLintJar)
Run Code Online (Sandbox Code Playgroud)

更新3

您还可以将Java lint项目添加为对其他项目的依赖项,以获得与更新2相同的效果.

在此输入图像描述

更新4

此后我写了一篇关于这个主题的博客文章https://medium.com/@mosesJay/writing-custom-lint-rules-and-integrating-them-with-android-studio-inspections-or-carefulnow-c54d72f00d30#. 3hm576b4f.

And*_*ann 2

但我不知道如何从这里开始

我建议你先写一个测试Detector。这是一个示例项目,演示了如何编写Detector测试[1]。这样您就可以尝试Detector根据自己的喜好进行调整。

如何检查方法上是否存在注释

我建议查看 Android 的默认检测器 [2]。在那里您很可能会找到一个好的起点。例如AnnotationDetector

并发出警告,使其在 Android Studio 中可见?

如果您将自定义规则正确集成到项目中,Lint 将为您发出警告。请查看此处 [3],了解有关如何在项目中集成自定义规则的不同选项。注意:据我所知,自定义规则的警告仅在运行相应的 Gradle 任务时才会报告。Android Studio 的“自动突出显示”不适用于自定义规则。

  1. https://github.com/a11n/CustomLintRules
  2. https://android.googlesource.com/platform/tools/base/+/master/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks
  3. https://github.com/a11n/android-lint/tree/master/6_application