Writing custom lint alerts to check for custom annotation

I wrote 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 { } 

Designed to annotate methods that can cause problems if they are called carelessly. I added an annotation handler to my project, but this is only a warning in the javac output log file. I want this warning to appear in Android Studio along with other lint warnings wherever a method with this annotation is called. This is why I am trying to write a custom lint rule. I have a 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 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 to check if an annotation exists for a method and trigger a warning that will be visible in Android Studio?

UPDATE

Here is my Detector class for those who want 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 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 regular validation using Android Studio validation by creating a lint.xml file in the root directory of your project and adding a custom lint rule:

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

Note that the problem tag identifier is the identifier provided in the first argument of the Issue.create () method in the CaimitoDetector class. You will also have to copy the jar file created when you created the lint rule to the / home / {user} /. Android / lint folder for it to work. For this, I wrote a custom gradle task. Here is my lint rule build.gradle file rule

 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 a Java lint project as a dependency on other projects to get the same effect as Update 2.

enter image description here

UPDATE 4

Since then I wrote a blog post on this subject https://medium.com/@mosesJay/writing-custom-lint-rules-and-integrating-them-with-android-studio-inspections-or-carefulnow-c54d72f00d30# .3hm576b4f .

+5
source share
1 answer

But I do not know how to get out of here

I suggest writing a test for your Detector . Here is an example project that demonstrates how to write Detector tests [1]. Thus, you can try and configure your Detector as you wish.

How to check if annotation exists for a method

I suggest taking a look at the default Android detectors [2]. There you are likely to find a good moment to start. For instance. AnnotationDetector .

and raise a warning so that it is visible in Android Studio?

If you integrate your own rules correctly into your project, Lint will raise a warning for you. Please see here [3] for different options for setting custom rules in your project. Note. AFAIK alerts for custom rules will only be displayed when the corresponding Gradle task is completed. "Auto-highlighting" Android Studio does not work with custom rules.

+2
source

Source: https://habr.com/ru/post/1258475/


All Articles