Escribir advertencia de pelusa personalizada para verificar la anotación personalizada

He escrito la siguiente anotación:

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 {

}

Que tiene como objetivo anotar métodos que pueden causar problemas si se llama descuidadamente. Agregué un procesador de anotaciones a mi proyecto, pero esto solo proporciona la advertencia en la salida del registro del comando javac. Quiero que esta advertencia aparezca en Android Studio junto con las otras advertencias de pelusa en cualquier lugar donde se llame un método con esta anotación. Es por eso que estoy tratando de escribir una regla de pelusa personalizada. Tengo el esqueleto básico de la regla de la pelusa:

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) {

  }

}
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);
  }

}

Pero no sé cómo proceder desde aquí. ¿Cómo puedo verificar si existe una anotación en un método y generar una advertencia para que sea visible en Android Studio?

ACTUALIZAR

Aquí está mi clase de Detector para cualquiera que quiera hacer lo mismo:

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;
    }

  }

}

ACTUALIZACIÓN 2

Puede integrar su comprobación de pelusa personalizada con las inspecciones de Android Studio creando un archivo lint.xml en la raíz de su proyecto y agregando su regla de pelusa personalizada de esta manera:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <issue id="Warning" severity="warning"/>
</lint>

Observe que la identificación de la etiqueta de problema es la identificación proporcionada en el primer argumento del método Issue.create () en la clase CaimitoDetector. También tendrá que copiar el archivo jar generado generando su regla de pelusa en su carpeta /home/{user}/.android/lint para que funcione. Escribí una tarea personalizada de gradle para esto. Aquí está el archivo build.gradle de mi regla de pelusa

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)

ACTUALIZACIÓN 3

También puede agregar su proyecto Java Lint como una dependencia de otros proyectos para obtener el mismo efecto que la actualización 2.

ACTUALIZACIÓN 4

Desde entonces he escrito una publicación de blog sobre este temahttps://medium.com/@mosesJay/writing-custom-lint-rules-and-integrating-them-with-android-studio-inspections-or-carefulnow-c54d72f00d30#.3hm576b4f .

Respuestas a la pregunta(1)

Su respuesta a la pregunta