Android Studio 中自己定义Lint(二)
一、Android lint uast介绍
UastScanner包含13个回调方法,下面介绍常用的几个:
1.getApplicableUastTypes
此方法返回需要检查的AST节点的类型,类型匹配的UElement将会被createUastHandler(createJavaVisitor)创建的UElementHandler(Visitor)检查。
2.createUastHandler
创建一个UastHandler来检查需要检查的UElement,对应于getApplicableUastTypes
3.getApplicableMethodNames
返回你所需要检查的方法名称列表,或者返回null,相匹配的方法将通过visitMethod方法被检查
4.visitMethod
检查与getApplicableMethodNames相匹配的方法
5.getApplicableConstructorTypes
返回需要检查的构造函数类型列表,类型匹配的方法将通过visitConstructor被检查
6.visitConstructor
检查与getApplicableConstructorTypes相匹配的构造方法
7.getApplicableReferenceNames
返回需要检查的引用路径名,匹配的引用将通过visitReference被检查
8.visitReference
检查与getApplicableReferenceNames匹配的引用
9.appliesToResourceRefs
返回需要检查的资源引用,匹配的引用将通过visitResourceReference被检查
10.visitResourceReference
检查与appliesToResourceRefs匹配的资源引用
11.applicableSuperClasses
返回需要检查的父类名列表,此处需要类的全路径名
11.visitClass
检查applicableSuperClasses返回的类
二、uast和psi对比
建议:
UastContext context = UastUtils.getUastContext(element); UExpression body = context.getMethodBody(method);
UastContext context = UastUtils.getUastContext(element); UExpression initializer = context.getInitializerBody(field);
Call names
In PSI, a call was represented by a PsiCallExpression, and to get to things like the method called or to the operand/qualifier, you'd first need to get the "method expression". In UAST there is no method expression and this information is available directly on the UCallExpression element. Therefore, here's how you'd change the code:
< call.getMethodExpression().getReferenceName(); --- > call.getMethodName()
Call qualifiers
Similarly,
< call.getMethodExpression().getQualifierExpression(); --- > call.getReceiver()
Call arguments
PSI had a separate PsiArgumentList element you had to look up before you could get to the actual arguments, as an array. In UAST these are available directly on the call, and are represented as a list instead of an array.
< PsiExpression[] args = call.getArgumentList().getExpressions(); --- > List args = call.getValueArguments();
Typically you also need to go through your code and replace array access, arg[i], with list access, arg.get(i). Or in Kotlin, just arg[i]...
Instanceof
You may have code which does something like "parent instanceof PsiAssignmentExpression" to see if something is an assignment. Instead, use one of the many utilities in UastExpressionUtils - such as UastExpressionUtils.isAssignment(UElement). Take a look at all the methods there now - there are methods for checking whether a call is a constructor, whether an expression is an array initializer, etc etc.
Android Resources
Don't do your own AST lookup to figure out if something is a reference to an Android resource (e.g. see if the class refers to an inner class of a class named "R" etc.) There is now a new utility class which handles this: ResourceReference. Here's an example of code which has a UExpression and wants to know it's referencing a R.styleable resource:
ResourceReference reference = ResourceReference.get(expression); if (reference == null || reference.getType() != ResourceType.STYLEABLE) { return; }