在总是抛出方法后如何检测死代码?

时间:2016-06-08 14:34:02

标签: java dead-code

考虑以下代码:

@Test 
public void testDeadCode() {
    letsThrow();
    System.out.println("will never be reached");
}

private final void letsThrow() {
    throw new RuntimeException("guess you didnt see this one coming");
}

对我而言,println()似乎绝对不可能执行 - 因为对letsThrow()的调用将总是抛出异常。

因此我

a)惊讶于编译器无法告诉我“这是死代码”

b)想知道是否有一些编译器标志(或eclipse设置)会告诉我:你那里有死代码。

3 个答案:

答案 0 :(得分:4)

死代码编译时错误由编译器而不是IDE定义。虽然代码永远不会被执行,但它并没有违反Oracle Docs中无法访问的语句的任何规则。

来自Unreachable Statements

  

本节致力于准确解释“可达”这个词。"我们的想法是,从构造函数,方法,实例初始化程序或包含语句本身的静态初始化程序开始,必须有一些可能的执行路径。分析考虑了陈述的结构。除了对while,do和for条件表达式具有常量值true的语句的特殊处理外,在流分析中不考虑表达式的值。

专门针对此案例的规则与您创建的块是否可访问有关。 (iff =当且仅当)

  

如果可以访问,那么非空交换块的空块可以正常完成。

     

非空交换块的非空块可以正常完成,如果其中的最后一个语句可以正常完成。

     

如果块可以访问,则非空块中不是交换块的第一个语句是可到达的。

     

如果S之前的语句可以正常完成,则非空交换块中的每个其他语句S都是可到达的。

letsThrow方法符合工作代码块的标准,技术上正常完成。它抛出一个异常,但它完成了。在确定是否实际使用该代码块时,不考虑它是否抛出保证异常,是否可以到达。在大多数情况下,只有在涉及try / catch / returns时才会找到死代码,这是规则的主要部分。

考虑以下,更简洁的版本:

@Test 
public void testDeadCode() {
    System.exit(0);
    System.out.println("will never be reached");
}

除了努力使用覆盖工具之外,没有真正的计数器,但是你的例子中明亮的一面是你每次运行代码时都会看到保证的例外。

答案 1 :(得分:0)

旨在进行全面的单元测试并测量测试的测试覆盖率。死代码会很明显,因为没有一个测试会导致它被执行。

答案 2 :(得分:0)

将您的方法声明为返回throwable类型:

private final RuntimeException letsThrow() {
  throw new RuntimeException("guess you didnt see this one coming");
}

然后你可以在打电话时抛出:

throw letsThrow();

现在,任何跟随letsThrow()调用的代码都将被检测为死亡。

您可以通过检查使用静态分析工具未使用letsThrow()的返回值的情况来强制执行此操作。例如,Google's errorprone has a checker for the @CheckReturnValue annotation确保您使用结果。

(对于穷人的版本,请搜索正则表达式^\s*letsThrow();)。