【问题标题】:False positive V595 The '_parent' pointer was utilized before it was verified against nullptr误报 V595 在针对 nullptr 进行验证之前使用了“_parent”指针
【发布时间】:2018-02-05 12:10:32
【问题描述】:

我将 PVS-Studio 用于我的项目 Torrent File Editor。有一个误报。 Here 没有真正的问题,但我得到这样的错误:

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

代码sn-p:

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 版本检查。在 Release 版本中不执行此类检查。对于 Release,我使用 _parent ? ... : ... 来防止可能的崩溃。所以在 Debug 版本中检查重复是完全可以的。

我通过特别评论来抑制这种误报。所以这不是问题,但认为 PVS-Studio 应该处理这种情况。

【问题讨论】:

  • 它可以与 assert() 一起使用吗?我记得 LLVM 的静态分析器也与 Q_ASSERT 混淆了
  • 跟 assert() 一样。
  • 如果:return _parent != nullptr ? _parent-&gt;child(row) : nullptr; 怎么办?
  • 不要再工作了。

标签: c++ qt pvs-studio


【解决方案1】:

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

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

但是,Q_ASSERT(_parent) 不保证指针 _parent 不为零。如果_parent 为零,则Q_ASSERT 语句将使用qFatal 函数输出以下消息。如果您使用默认消息处理程序,此函数将中止创建核心转储。

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

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

Q_ASSERT(_parent);
Q_ASSERT(row < _parent->childCount());

_parent-&gt;childCount() 函数调用永远不会执行,如果指针 _parent 等于 nullptr。如果指针为空,那么程序会因为调用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;
}

最简单但不是最好的方法是使用 cmets 将警告显式标记为 false:

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。另一种选择是使用诊断配置文件 (pvsconfig)。所有这些方法都在“Suppression of false alarms”部分的文档中进行了详细描述。 markup base 也存在。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-25
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多