简介
在自定义Lint检测Detector时,我们有时候会在override fun createUastHandler(context: JavaContext): UElementHandler?
中创建一个自定义的UElementHandler进行后续的检测,它内部定义了非常多的visitXX
接口用于访问不同的Element
(访问者模式)。
其中,override fun visitCallExpression(node: UCallExpression)
可以用来检测所有的代码表达式,如果我们希望将node解析为一个方法时,就需要用到这篇文章的主角了——UCallExpression.resolve()
方法作用
UCallExpression.resolve()
返回值为com.intellij.psi.PsiMethod
,从它里面我们可以对该方法做大量判断,例如:
- PsiType getReturnType():获取返回值类型
- PsiReferenceList getThrowsList():获取抛出问题列表
- boolean isConstructor():是否是构造方法
- PsiMethod[] findSuperMethods():获取super方法
- JvmParameter[] getParameters():获取参数
- getAnnotations():获取注解
- boolean isDeprecated():是否是过时方法
PsiMethod如此强大,如果想做关于方法的检测,基本上只要获取到PsiMethod就可以实现。但是这里有坑了,有时候我们执行node.resolve的结果却是null,这是为什么呢?
resolve==null的几种情况
非全量lint
有时候我们执行lint时是通过LintCliClient.run(LintIssueRegistry, List<File>)
方法启动的,这里我们是传入了一个待检测文件列表,Lint只会检查传入的文件。
假设我们传入了一个A类,里面调用了B.b()
方法,如果B类没有包含在List中传入LintCliClient的话,b()方法是无法识别的,因为没有B类,LintCliClient也不知道b是什么,所以resolve会是null。
无法识别参数的方法
- 与R文件引用有关的方法,例如:
- setContentView(R.layout.xx)
- LayoutInflater.inflate(R.layout.xx)
- Context.getString(R.string.xx)
- EditText.setHint(R.string.xx)
- TextView.setText(R.string.xx)
- 变长参数方法: A.a(B… bs)
kotlin相关
- 使用了别名类TypeAliases.kt提供的ArrayList、HashMap、LinkedHashMap、HashSet、LinkedHashSet进行类构造;
- kotlin中使用了java类的构造方法(不限于继承时的构造方法申明)
- 使用了ArrayList.isNullOrEmpty()等inline扩展方法
node自身就是null
这个。。确实有这样的情况,但是我暂时没能定位到该node具体是什么,没有找到规律。
结论
通过以上测试,我们发现node.resolve==null的情况大多数都是与环境、android sdk api或者kotlin语言有关,所以对于我们自己代码的检测可以直接忽略这种情况,在Google官方提供的Lint检测库中也是采用了忽略的策略。