如何在Java中找到潜在的未经检查的异常?

ocr*_*tte 6 java static-analysis exception-handling software-quality exception

根据Java规范,Java编译器根据"throw"语句和方法签名自动验证是否捕获了所有已检查的异常,并忽略未经检查的异常.

但是,有时开发人员可以找出可以抛出哪些未经检查的异常,例如某些第三方代码可能会在开发人员倾向于期望检查异常(如Long.parseLong)的情况下抛出未经检查的异常.或者开发人员可能会将未经检查的异常作为未来检查异常的占位符而忘记替换它.

在这些示例中,理论上可以找到这些未被捕获的未经检查的异常.在第一种情况下,Long.parseLong的签名表示它抛出NumberFormatException,在第二种情况下,源代码可用,因此编译器知道抛出了哪些未经检查的异常.

我的问题是:是否有可以报告这些案例的工具?或者也许让Java编译器处理暂时未经检查的异常的方法是检查异常?这对于手动验证并修复可能导致整个线程或应用程序在运行时崩溃的潜在错误非常有用.

编辑:在得到一些答案之后,我必须强调我的目标不是找到系统中可能存在的未经检查的异常的详尽列表,而是由于未经检查的异常导致的潜在错误.我认为归结为两种情况:

  1. 方法的签名表示它抛出一个未经检查的异常,并且调用者没有捕获它
  2. 方法的主体显式抛出未经检查的异常,并且调用者不会捕获它们

Ben*_*and 6

是的,您可以编写静态分析来执行此操作。我自己也做过类似的事情,并在一个名为Atlas的程序分析工具中写了我的。此处:https://github.com/EnSoftCorp/java-toolbox-commons/.../ThrowableAnalysis.java是一些可能对您有帮助的代码,它静态计算抛出站点和潜在捕获站点的匹配项一个软件(保守的,因为它不考虑路径可行性)。对于您的情况,您对没有相应 catch 块的 throw 站点感兴趣。

以下是分析的重要部分。

  1. 所有检查或未检查的异常都必须扩展Throwable。听起来您只对“未经检查的”可抛出对象感兴趣,因此您应该考虑直接扩展的类或者是扩展ErrorRuntimeException的类的子类。

可抛出的层次结构

在 Atlas Shell 中,您可以编写以下查询来查找所有未检查的 throwable。

var supertypeEdges = Common.universe().edgesTaggedWithAny(XCSG.Supertype)
var errors = supertypeEdges.reverse(Common.typeSelect("java.lang", "Error"))
var uncheckedExceptions = supertypeEdges.reverse(Common.typeSelect("java.lang", "RuntimeException"))
show(errors.union(uncheckedExceptions))
Run Code Online (Sandbox Code Playgroud)
  1. 任何可以在运行时捕获的异常(已检查或未检查)都必须具有相应的“抛出”站点。虽然抛出的已检查异常必须在方法签名中声明,但不需要为抛出的未检查异常声明。然而,这并不是那么重要,因为我们可以通过查看我们在步骤 1 中讨论的类型层次结构来检测所有抛出的未经检查的异常。

  2. 要将 throw 站点与相应的 catch 块匹配,我们必须记住,抛出的异常会传播到调用堆栈,直到它被捕获(或者当它没有被 main 方法或线程入口点捕获时使程序崩溃)。要进行此分析,您需要一个调用图(调用图越精确,您的分析就越准确)。对于每次抛出未经检查的异常类型,沿着调用图向后移动到可能抛出未经检查的异常的方法的调用点。检查 callsite 是否包含在 try 块中(如果您正在分析字节码,则检查是否有陷阱区域)。如果是,则必须检查 catch 块/陷阱区域的兼容性并确定是否会捕获异常。

使用我之前分享的ThrowableAnalysis代码,您可以将它们组合在一起以找到每个未捕获的未检查抛出的可抛出类型。

public class Analysis {

    // execute show(Analysis.run()) on the Atlas shell
    public static Q run(){
        Q supertypeEdges = Common.universe().edgesTaggedWithAny(XCSG.Supertype);
        Q errors = supertypeEdges.reverse(Common.typeSelect("java.lang", "Error"));
        Q uncheckedExceptions = supertypeEdges.reverse(Common.typeSelect("java.lang", "RuntimeException"));
        Q typeOfEdges = Common.universe().edgesTaggedWithAny(XCSG.TypeOf);
        Q thrownUncheckedThrowables = typeOfEdges.predecessors(errors.union(uncheckedExceptions)).nodesTaggedWithAny(XCSG.ThrownValue);

        AtlasSet<Node> uncaughtThrownUncheckedThrowables = new AtlasHashSet<Node>();
        for(Node thrownUncheckedThrowable : thrownUncheckedThrowables.eval().nodes()){
            if(ThrowableAnalysis.findCatchForThrows(Common.toQ(thrownUncheckedThrowable)).eval().nodes().isEmpty()){
                uncaughtThrownUncheckedThrowables.add(thrownUncheckedThrowable);
            }
        }

        Q uncaughtThrownUncheckedThrowableMethods = Common.toQ(uncaughtThrownUncheckedThrowables).containers().nodesTaggedWithAny(XCSG.Method);
        Q callEdges = Common.universe().edgesTaggedWithAny(XCSG.Call);
        Q rootMethods = callEdges.reverse(uncaughtThrownUncheckedThrowableMethods).roots();
        Q callChainToUncaughtThrowables = callEdges.between(rootMethods, uncaughtThrownUncheckedThrowableMethods);
        return callChainToUncaughtThrowables.union(Common.toQ(uncaughtThrownUncheckedThrowables));
    }

}
Run Code Online (Sandbox Code Playgroud)

这是在以下测试用例上运行此代码的结果的屏幕截图。

public class Test {

    public static void main(String[] args) {
        foo();
    }

    private static void foo(){
        throw new Pig("Pigs can fly!");
    }

    public static class Pig extends RuntimeException {
        public Pig(String message){
            super(message);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

分析结果截图

重要警告:您必须考虑是否在这里进行整个程序分析。如果您只分析您的代码而不是完整的 JDK(几百万行代码),那么您将只能检测到源自您的应用程序内部的未捕获的运行时异常。例如,您不会捕获 "test".substring(0,10),它会在 JDK 的 String 类中声明的 substring 方法中抛出越界异常。虽然 Atlas 支持使用 Java 源代码或字节码进行部分或整个程序分析,并且可以扩展到完整的 JDK,但如果您计划包含完整的 JDK,则需要分配大约 1 小时的预处理时间和 20 GB 的内存。


Tho*_*gor 5

有一个Intellij插件可以帮助您发现未经检查的异常.您可以自定义搜索过程以在搜索时包含/排除库.

https://plugins.jetbrains.com/plugin/8157?pr=