自定义lint_如何在android中实现自定义lint规则以警告从抛出的检查异常

自定义lint

While Java introduced the new concept of checked exceptions a few decades ago, Kotlin decided against using it, handling all exceptions as unchecked ones. This can become a point of failure when you are developing an application with both Java and Kotlin code. In this article, we are going to implement an opinionated custom lint rule in order to fill this gap in expectations between these two languages.

Java在几十年前引入了检查异常的新概念时,Kotlin决定不使用它,而是将所有异常都视为未检查异常。 当您同时使用Java和Kotlin代码开发应用程序时,这可能会成为故障点。 在本文中,我们将实现自定义的自定义皮棉规则,以填补这两种语言之间的期望差距。

不要从Kotlin代码抛出异常 (Do not throw Exception from Kotlin code)

As all Exceptions in Kotlin are unchecked, we should not rely on them to return context to the callers. We should only use them for truly unexpected errors: the good news is that Java already has a class for this type of exceptions, which is the RuntimeException class.

由于Kotlin中的所有Exception都未选中,因此我们不应该依赖于它们将上下文返回给调用方。 我们只应将它们用于真正的意外错误:好消息是Java已经有一个用于此类异常的类,即RuntimeException类。

Therefore, for this lint check, we want to detect if our Kotlin code throw Exceptions, either by actually creating and throwing one, or by annotating a method with the @Throws annotation (of course there are perfectly valid cases to annotate a Kotlin method as @Throws, e.g. when it will be called from Java, but as these should be not our common case, we can suppress them individually).

因此,对于此棉绒检查,我们想通过实际创建并抛出一个Kotlin代码,或者通过使用@Throws注释对方法进行注释来检测Kotlin代码是否抛出异常(当然,在将Kotlin方法注释为@Throws,例如何时从Java调用它,但是由于这不是我们常见的情况,因此我们可以单独禁止它们。

As in the other articles of this series (part 1), we start by declaring the Implementation and the Issue, which encapsulates the Lint error, including the description we want to show for developers. We are considering this issue to be an Error, so it is more severe than other lint warnings.

与本系列的其他文章(第1部分)一样,我们从声明实现和问题开始,其中封装了Lint错误,包括我们要向开发人员展示的描述。 我们认为此问题为Error ,因此比其他棉绒警告更严重。

val IMPLEMENTATION = Implementation(
    KotlinShouldNotThrowExceptionsDetector::class.java,
    Scope.JAVA_FILE_SCOPE
)


val ISSUE: Issue = Issue.create(
    id = "KotlinShouldNotThrowExceptionsDetector",
    briefDescription = "Kotlin code should not throw Exceptions",
    explanation = """
        Kotlin does not support checked Exceptions, so we should not rely on Exceptions to propagate errors in our application.
        More details in: https://kotlinlang.org/docs/reference/exceptions.html
    """.trimIndent(),
    category = Category.CORRECTNESS,
    priority = 9,
    severity = Severity.ERROR,
    androidSpecific = true,
    implementation = IMPLEMENTATION
)


fun reportUsage(context: JavaContext, location: Location) {
    context.report(
        issue = ISSUE,
        location = location,
        message = "Kotlin code should not throw Exceptions"
    )
}

Our detector class is interested both in methods and in call expressions. The first one, to identify the @Throws annotation, and the second one, to identify throw expressions. For the former, we can simply check, when visiting a method in line 11, if it contains the @Throws annotation; if positive, we can report this as a lint error. For the latter, we create a separate visitor (line 20) to encapsulate its slightly more complex logic.

我们的检测器类对方法和调用表达式都感兴趣。 第一个用于标识@Throws批注,第二个用于标识throw表达式。 对于前者,我们可以在访问第11行中的方法时简单地检查它是否包含@Throws批注; 如果为正,我们可以将其报告为皮棉错误。 对于后者,我们创建一个单独的访问者(第20行)以封装其稍微复杂的逻辑。

override fun getApplicableUastTypes(): List<Class<out UElement>>? {
    return listOf(UMethod::class.java, UCallExpression::class.java)
}


override fun createUastHandler(context: JavaContext): UElementHandler? {
    return KotlinThrowsHandler(context)
}


private inner class KotlinThrowsHandler(private val context: JavaContext) : UElementHandler() {


    override fun visitMethod(node: UMethod) {
        for(annotation in node.annotations) {
            if(annotation.qualifiedName == Throws::class.qualifiedName) {
                reportUsage(context, location = context.getLocation(node = node))
            }
        }
    }


    override fun visitCallExpression(node: UCallExpression) {
        node.accept(ThrowCallVisitor(context))
    }


}

This visitor is responsible to identify throw expressions in our code (line 4), and then check if the exception this expression is throwing is a checked or a an unchecked exception. If it is an unchecked, there is nothing we should do. Otherwise, we report this as a lint warning, as this is exactly the case we want to detect (Kotlin code throwing checked exceptions).

该访问者负责在我们的代码中识别出throw表达式(第4行),然后检查此表达式所抛出的异常是已检查的异常还是未检查的异常。 如果未经检查,则我们无能为力。 否则,我们会将其报告为棉绒警告,因为这正是我们要检测的情况(Kotlin代码抛出已检查的异常)。

Finally, to check if an Exception is an unchecked one, we need to verify if its type is RuntimeException (line 14), or if it inherits from it (line 17–18, using recursion).

最后,要检查Exception是否为未检查的Exception,我们需要验证其类型是否为RuntimeException(第14行),或者是否从其继承(第17-18行,使用递归)。

private inner class ThrowCallVisitor(private val context: JavaContext) : AbstractUastVisitor() {


  override fun visitCallExpression(node: UCallExpression): Boolean {
      if(node.uastParent is KotlinUThrowExpression) {
          val type = node.returnType ?: throw IllegalStateException("type of KotlinUThrowExpression cannot be null")
          if(!isUncheckedException(type)) {
              reportUsage(context, context.getLocation(element = node))
          }
      }
      return false
  }


  private fun isUncheckedException(type: PsiType): Boolean {
      if(type.canonicalText == RuntimeException::class.java.canonicalName) {
          return true
      }
      for(superType in type.superTypes) {
          if(isUncheckedException(superType)) {
              return true
          }
      }
      return false
  }


}

And done! The complete code can be found here, and the complete set of unit tests here.

并做了! 完整的代码可以发现在这里,和成套单元测试在这里

结论 (Conclusion)

You can find the repository with all the examples here. Please fell free to share if you think these rules are useful, and also if you have some ideas of other custom lint checks.

您可以在此处找到包含所有示例存储库。 如果您认为这些规则有用,并且对其他自定义皮棉检查有一些想法,请随意分享。

This article is part of a series about creating custom lint rules in Android:Part 1: How to implement your first custom lint rule in Android using TDDPart 2: How to implement a custom lint rule in Android that requires an overall view of the projectPart 3: How to implement a custom lint rule in Android to warn against checked exception thrown from Kotlin Part 4: How to implement a custom lint rule in Android to handle differences in Exception handling between Java and KotlinPart 5: How to implement a custom lint rule in Android to detect if we are setting the fragment manager before calling Activity onCreate methodPart 6: (Maybe) How to implement a custom lint rule in Android to detect if we are passing an immutable collection to a Java method which needs a mutable one

本文是有关在Android中创建自定义棉绒规则的系列文章的一部分:第1部分:如何使用TDD在Android中实现您的第一个自定义棉绒规则第2部分:如何在需要项目总体视图的Android中实现自定义棉绒规则第3部分:如何在Android中实现自定义棉绒规则以警告Kotlin引发的已检查异常第4部分:如何在Android中实现自定义棉绒规则以处理Java和Kotlin之间的异常处理差异第5部分:如何实现自定义棉绒Android中的规则以检测是否在调用Activity onCreate方法之前设置了片段管理器第6部分:(也许)如何在Android中实现自定义的lint规则以检测我们是否将不可变的集合传递给需要可变变量的Java方法

翻译自: https://medium.com/@guilhermekrz/how-to-implement-a-custom-lint-rule-in-android-to-warn-against-checked-exception-thrown-from-a076eb9fecd5

自定义lint

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值