一、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;? ? ? ? }