误报 V595 '_parent' 指针在根据 nullptr 进行验证之前被使用

False positive V595 The '_parent' pointer was utilized before it was verified against nullptr

我在我的项目中使用 PVS-Studio Torrent File Editor. There is one false positive. Here 没有真正的问题,但我收到这样的错误:

torrent-file-editor/abstracttreenode.h:138: error: V595 The '_parent' pointer was utilized before it was verified against nullptr. Check lines: 138, 139.

代码片段:

inline T *sibling(int row) const
{
    Q_ASSERT(_parent);
    Q_ASSERT(row < _parent->childCount()); // -V595 PVS-Studio
    return _parent ? _parent->child(row) : nullptr;
}

这里Q_ASSERT只是Debug版本检查。发布版本不执行此类检查。对于发布,我使用 _parent ? ... : ... 来防止可能的崩溃。所以在Debug版本中check duplicate是完全OK的。

我用特殊评论抑制了这个误报。所以这不是问题,但认为 PVS-Studio 应该处理这种情况。

V595 诊断逻辑很简单。如果在开始时取消引用指针,然后验证是否与 nullptr 相等,则会发出警告。

当然有很多情况下分析仪会安静下来,遇到这样的模式。包括指针不等于nullptr的情况,分析器会保持安静。

但是,Q_ASSERT(_parent) 不保证指针 _parent 不为零。如果 _parent 为零,则 Q_ASSERT statement will output the following message using the qFatal 函数。如果您使用的是默认消息处理程序,则此函数将中止以创建核心转储。

您可以安装自己的处理程序,它将继续 运行 程序。所以理论上分析器是正确的。可能会取消引用空指针。

我们不是理论家而是实践家,我们意识到这个代码应该被认为是正确的。分析器不熟悉这样的代码视图,其中使用了宏 Q_ASSERT。我们将修改分析器,使其开始将此类代码模式视为正确的。 IE。将来分析器会假设这里:

Q_ASSERT(_parent);
Q_ASSERT(row < _parent->childCount());
如果指针 _parent 等于 nullptr,则

_parent->childCount() 函数调用永远不会执行。如果指针为空,那么程序会因为调用 qFatal().

而提前停止工作

当然,正如我上面已经说过的,你可以改变处理程序的行为,它不会导致程序中止。但是,实际上,没有人会更改处理程序并编写我们在这里考虑的代码。

这可能是答案的终点。所以,我们将改进分析器,仅此而已。然而,不可能预见所有可能的选择。如果是我们自己的宏,如何抑制警告?

假设这个自制的错误记录系统和分析器对自定义函数一无所知Foo()

void Foo(bool expr);
#define Q_ASSERT(expr) Foo(expr);

inline T *sibling(int row) const
{
  Q_ASSERT(_parent);
  Q_ASSERT(row < _parent->childCount())
  return _parent ? _parent->child(row) : nullptr;
}

最简单但不是最好的方法是使用注释将警告明确标记为错误:

Q_ASSERT(row < _parent->childCount())   //-V595

另一种方案是改变写代码的风格,写成如下:

inline T *sibling(int row) const
{
  if (_parent == nullptr)
  {
    Q_ASSERT(false);
    return nullptr;
  }
  Q_ASSERT(row < _parent->childCount());
  return _parent->child(row);
}

对于这样的代码,分析器不会发出警告 V595,因为没有理由这样做。代码变长了一点,但在我看来,它现在在逻辑上更加正确和安全。我推荐这种处理此类警告的方式。

最后是在宏中使用警告抑制机制。要在定义宏的头文件中做到这一点,你应该写注释:

//-V:Q_ASSERT:595

此警告将消失。当然,并不总是可以更改声明宏的文件。然后,您可以使用其中一个全局文件。在 Visual C++ 项目中,一个很好的候选者是 stdafx.h. Another option is to use the diagnostic configuration files (pvsconfig). All these methods are described in detail in documentation in section "Suppression of false alarms". The markup base 也存在。