Android Lint自定义检查方法接收者的UCallExpression类型

时间:2018-12-19 08:44:36

标签: java android kotlin lint android-lint

我正在编写自定义的棉绒检查以禁止某些方法。因此,如果有人在类foo的实例上调用了被禁止的方法A,则皮棉应该报告错误。

我是通过这样的静态方法(在visitCallExpression(UCallExpression)内部实现的

node.receiver as UReferenceExpression).getQualifiedName()

从限定名称中,我可以获取Class对象并运行检查,但是对于在实例化对象上调用的方法,我无法获得限定名称。我可以获取对象所属的类的名称,但不能获取限定名称。

如何获取在该类的实例上调用的方法的类的合格名称?如果不清楚,请举一个例子。

import android.view.Button;

class ButtonSetTextIntClass {
       private Button button;

       public void bannedSetText (){
            button.setText(123);
       }
}

我需要在visitCallExpression (UCallExpression)中获得button的合格名称/类。

3 个答案:

答案 0 :(得分:0)

UCallExpression.receiverType does what you want:

public class CustomDetector extends Detector implements SourceCodeScanner {

    @Nullable
    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.singletonList(UCallExpression.class);
    }

    @Nullable
    @Override
    public UElementHandler createUastHandler(@NotNull JavaContext context) {
        return new UElementHandler() {

            @Override
            public void visitCallExpression(@NotNull UCallExpression node) {
                node.getReceiverType(); // PsiType:Button
            }
        };
    }
}

To extract qualified name you can use the following method:

((PsiClassType) node.getReceiverType()).resolve().getQualifiedName() // android.widget.Button

答案 1 :(得分:0)

我找到了一个适用于静态和非静态方法以及Kotlin顶级功能的解决方案。不确定这是否是最好的方法,但至少能奏效。

override fun visitCallExpression(node: UCallExpression) {
     (node.resolve()?.parent as? ClsClassImpl)?.stub?.qualifiedName
}

答案 2 :(得分:0)

    /**
     * eg: android.util.Log
     * ps. imports was initialized in visitImportStatement
     */
    private fun getClassNameWithPackage(node: UCallExpression): String {
        var className = node.resolve()?.containingClass?.qualifiedName
        if (className != null) {
            return className
        }

        className = getClassName(node) ?: return ""
        for (import in imports) {
            if (import.contains(className)) {
                return import
            }
        }
        return "$packageName.$className"
    }

    /**
     * eg: Log
     */
    private fun getClassName(node: UCallExpression): String? {
        return node.receiver?.javaPsi?.text ?: when (val uExpression = (node.methodIdentifier?.uastParent as UCallExpression?)?.receiver) {
            is JavaUSimpleNameReferenceExpression -> {
                uExpression.identifier
            }
            is KotlinUSimpleReferenceExpression -> {
                uExpression.identifier
            }
            is UReferenceExpression -> {
                uExpression.getQualifiedName()
            }
            else -> null
        }
    }