1

我在一个大型遗留 Java 8 (Android) 应用程序上工作。我们最近发现了一个由方法的忽略结果引起的错误。具体来说,方法的调用者send()在发送失败时没有采取正确的行动。它已被修复,但现在我想添加一些静态分析来帮助查找我们的代码中是否存在其他相同性质的现有错误。此外,为了防止将来添加相同性质的新错误。

我们已经使用 Find Bugs、PMD、Checkstyle、Lint 和 SonarQube。所以我认为其中一个可能已经具有我正在寻找的检查,但只需要启用它。但是经过几个小时的搜索和测试,我认为情况并非如此。

作为参考,这是我正在测试的代码:

public class Application {
    public status void main(String[] args) {
        foo(); // I want this to be caught
        Bar aBar = new Bar();
        aBar.baz(); // I want this to be caught
    }
    
    static boolean foo() {
        return System.currentTimeMillis() % 2 == 0;
    }
}

public class Bar {
    boolean baz() {
        return System.currentTimeMillis() % 2 == 0;
    }
}

我想在调用者方面捕捉到这一点,因为一些调用者可能会使用该值,而其他调用者则不会。(上述send()方法就是这种情况)

我发现了以下现有的静态分析规则,但它们似乎只适用于非常特定的情况,以避免误报并且不适用于我的示例:

到目前为止,最好的选择似乎是#3,但它需要我在我的巨大项目中注释每个方法或类。Java 9+ 似乎允许在包级别进行注释,但这对我来说不是一个选择。即使是这样,该项目也有很多包。我真的想要一种方法来配置它以通过一个/几个位置应用于我的整个项目,而不是需要修改每个文件。

最后,我遇到了这个 Stack Overflow答案,它告诉我 IntelliJ 使用“报告所有忽略的非库调用”检查进行了此检查。就在 IDE 中突出显示而言,这样做似乎有效。但我希望这会导致 CI​​ 失败。我发现有一种方法可以使用 intelliJ 工具通过命令行触发它,但这仍然会输出一个 XML/JSON 文件,我需要编写自定义代码来解析该输出。我还需要在 CI 机器上安装 IDE 工具,这似乎有点矫枉过正。

有谁知道实现我想要的更好的方法?我不能成为第一个只关心假阴性而不关心假阳性的人。我觉得应该可以管理任何当前未使用的返回值被记录或明确声明返回值通过注释故意忽略它或分配给变量约定,就像他们在容易出错时所做的那样

4

2 回答 2

1

像您描述的那种情况总是会导致严重的软件缺陷(在各个方面都是真正的错误);变得更加令人沮丧和棘手,因为代码默默地失败了,这使得问题保持隐藏。你想找出任何类似的隐藏缺陷(并纠正它们)的愿望很容易理解;但是,(我谦虚地建议)静态代码分析可能不是最好的策略:

  • 从您在问题中表达的关注点出发:一条CheckReturnValue规则极有可能产生一系列//Ignore代码注释、规则violationSuppress子句和/或注释,这些代码注释、规则子句和/或@suppressRule注释的数量远远超过规则的积极缺陷检测计数。

  • 在考虑 Java 垃圾收集并评估垃圾收集如何影响软件开发之后,Java 编程语言进一步增加了高规则抑制计数的可能性。从 Java 垃圾收集基于对象实例引用计数的理解出发,只有引用计数为 0(零)的实例才有资格进行垃圾收集,Java 开发人员避免不必要的引用并自然采用忽略不重要的方法调用返回值的做法。被忽略的实例将简单地从本地调用堆栈中脱落,大多数将达到 0(零)的引用计数,立即成为合格并快速进行垃圾收集。

现在从消极的角度转变为积极的观点,我提供替代方案供您考虑,(我相信)将改善您的结果,以及您获得成功结果的可能性。

  • 根据您对场景的描述和由此产生的缺陷/错误,感觉问题的最根本原因是单元测试失败或集成测试失败。发送操作的实现可能(并且几乎肯定会在某些时候)失败,单元测试和集成测试绝对应该包含多种可能的失败场景并验证失败场景处理。我显然不知道,但我敢打赌,如果你专注于创建和运行单元测试和集成测试,系统的质量会在每一步都得到提升,改进会很明显,而且你可能会非常找出导致您当前担忧、恐惧、压力和担忧的部分或全部隐藏错误。

  • 考虑保持当前静态代码分析研究的要点,但将方法转向新的方向。第一次阅读您的问题时,我意识到您要执行的代码检查存在于整个代码库中的多个不相关的位置,并且很快变得过于复杂,检查的具体细节在许多部分都不同代码,并且每个特殊情况都使整体工作变得不切实际。基本上,您想要实现的内容代表了一个跨领域的目标,该目标跨越了代码库的相当大一部分,而实现细节使一个相当简单的好主意变得异常复杂。您的问题几乎是一个问题的教科书示例,最好采用横切的面向方面的方法来实现。

如果您有时间和兴趣,请看一下AspectJ框架,也许编写一些探索性方面的代码,并让我知道您的想法。我想听听你的想法,如果你想在某个时候进行一次极客的开发对话。我真的希望这会有所帮助-

于 2020-08-07T08:17:59.410 回答
0

您可以使用 intelliJ IDEA 的检查:Java | 可能的错误 | 启用“报告所有忽略的非库调用”选项时忽略方法调用的结果。它捕获了您的代码示例中提供的两种情况。

于 2020-08-07T09:55:05.587 回答