【问题标题】:Android lint custom check UCallExpression type of method receiverAndroid lint 自定义检查 UCallExpression 类型的方法接收器
【发布时间】:2023-03-29 00:47:01
【问题描述】:

我正在编写自定义 lint 检查以禁止某些方法。所以如果有人在 A 类的实例上调用了被禁止的方法 foo,lint 应该会报错。

我通过这样的静态方法实现了这一点(在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 的限定名称/类。

【问题讨论】:

    标签: java android kotlin lint android-lint


    【解决方案1】:

    UCallExpression.receiverType 做你想做的事:

    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
                }
            };
        }
    }
    

    要提取限定名称,您可以使用以下方法:

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

    【讨论】:

    • 不幸的是,这不适用于 Kotlin 顶级函数。如果您有调用被禁止方法的顶级函数,则无法获取被禁止方法的限定名称。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:
          /**
           * 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
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-06
        • 1970-01-01
        • 2021-06-21
        • 1970-01-01
        • 1970-01-01
        • 2019-09-25
        相关资源
        最近更新 更多