I have written the following annotation:
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 {
}
Which is intended to annotate methods which can cause problems if called carelessly. I added an annotation processor to my project, but this only provides the warning in the log output of the javac command. I want this warning to appear in Android Studio along with the other lint warnings anywhere a method with this annotation is called. This is why I am trying to write a custom lint rule. I have the basic skeleton of the lint rule:
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);
}
}
But I do not know how to proceed from here. How can I check if an annoation exists on a method, and raise a warning such that it will be visible in Android Studio?
UPDATE
Here is my Detector class for anyone looking to do the same:
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;
}
}
}
UPDATE 2
You can integrate your custom lint check with Android Studio inspections by creating a lint.xml file in the root of your project and adding you custom lint rule there like this:
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="Warning" severity="warning"/>
</lint>
Notice the id of the issue tag is the id provided at the first argument of the Issue.create() method in the CaimitoDetector class. You will also have to copy the jar file outputted by building your lint rule into your /home/{user}/.android/lint folder to make it work. I wrote a custom gradle task for this. Here is my lint rule's build.gradle file
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)
UPDATE 3
You can also add your Java lint project as a dependency on other projects to get the same effect as update 2.
UPDATE 4
I have since written a blog post on this topic https://medium.com/@mosesJay/writing-custom-lint-rules-and-integrating-them-with-android-studio-inspections-or-carefulnow-c54d72f00d30#.3hm576b4f .
But I do not know how to proceed from here
I suggest to write a test for your Detector
first. Here is an example project which demonstrates how to write Detector
tests [1]. That way you can try and adjust your Detector
as you like.
How can I check if an annoation exists on a method
I suggest to have a look at Android's default detectors [2]. There you'll most probably find a good point to start. E.g. the AnnotationDetector
.
and raise a warning such that it will be visible in Android Studio?
If you integrate your custom rules correctly into your project, then Lint will raise the warning for you. Please have a look here [3] for different options on how to integrate custom rules in your project. Note: AFAIK warnings of custom rules will only reported when running the corresponding Gradle task. The "auto-highlight" of Android Studio does not work with custom rules.
来源:https://stackoverflow.com/questions/40133113/writing-custom-lint-warning-to-check-for-custom-annotation