首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在PVS-Studio中的BOOST_REQUIRE之后将变量标记为非空

在PVS-Studio中的BOOST_REQUIRE之后将变量标记为非空
EN

Stack Overflow用户
提问于 2017-11-14 18:28:40
回答 2查看 264关注 0票数 2

我正在使用PVS-Studio来分析我的测试代码。通常存在以下形式的构造:

代码语言:javascript
复制
const noAnimal* animal = dynamic_cast<noAnimal*>(...);
BOOST_REQUIRE(animal);
BOOST_REQUIRE_EQUAL(animal->GetSpecies(), ...);

但是,我仍然会收到最后一行的警告V522 There might be dereferencing of a potential null pointer 'animal'

我知道可以将函数标记为“不返回NULL”,但是否也可以将函数标记为有效的NULL检查,或者让PVS-Studio以某种方式意识到在BOOST_REQUIRE(animal);之后animal不能为NULL

如果指针首先通过任何assert风格进行检查,也会发生这种情况。

EN

回答 2

Stack Overflow用户

发布于 2017-11-15 21:02:42

感谢您的有趣示例。我们将思考,我们可以对BOOST_REQUIRE宏做些什么。

目前,我可以建议您以下解决方案:

之后的某处

代码语言:javascript
复制
#include <boost/test/included/unit_test.hpp>

你可以这样写:

代码语言:javascript
复制
#ifdef PVS_STUDIO
  #undef BOOST_REQUIRE
  #define BOOST_REQUIRE(expr) do { if (!(expr)) throw "PVS-Studio"; } while (0)
#endif

这样,您将向分析器发出一个提示,即false条件会导致控制流中止。这不是最好的解决方案,但我认为它值得告诉你。

票数 1
EN

Stack Overflow用户

发布于 2017-11-16 16:49:23

回复一个大的评论不是一个好主意,所以下面是我对以下主题的详细回应:

虽然这是可能的,但在所有测试用例文件中包含定义将是一件痛苦的事情。此外,这不仅限于BOOST_REQUIRE,还适用于assert、SDL_Assert或用户可能使用的任何其他自定义宏。

人们应该理解,有三种类型的测试宏,每一种都应该单独讨论。

第一种类型的宏只是警告您在Debug版本中出现了错误。一个典型的例子是assert宏。以下代码将导致PVS-Studio分析器生成警告:

代码语言:javascript
复制
T* p = dynamic_cast<T *>(x);
assert(p);
p->foo();

分析器将在此处指出可能的空指针取消引用,并将是正确的。使用assert的检查是不够的,因为它将从发布版本中删除。也就是说,结果是没有支票。实现它的一种更好的方法是将代码重写为如下所示:

代码语言:javascript
复制
T* p = dynamic_cast<T *>(x);
if (p == nullptr)
{
  assert(false);
  throw Error;
}
p->foo();

此代码不会触发警告。

您可能会争辩说,您100%确定dynamic_cast永远不会返回nullptr。我不接受这个论点。如果您完全确定强制转换总是正确的,那么您应该使用速度更快的static_cast。如果您不确定,则必须在取消引用指针之前对其进行测试。

好吧,好吧,我明白你的意思。您确信代码是正确的,但您需要使用dynamic_cast进行检查,以防万一。好的,然后使用下面的代码:

代码语言:javascript
复制
assert(dynamic_cast<T *>(x) != nullptr);
T* p = static_cast<T *>(x);
p->foo();

我不喜欢它,但至少它更快,因为在发布版本中将忽略较慢的dynamic_cast操作符,而分析器将保持沉默。

继续讨论下一种类型的宏。

第二种类型的宏只是警告您在Debug版本中出现了错误,并在测试中使用。它们与前一种类型的不同之处在于,如果条件为false,则它们会停止测试中的算法,并生成错误消息。

这些宏的基本问题是函数没有被标记为不返回。下面是一个例子。

假设我们有一个通过抛出异常来生成错误消息的函数。它的声明是这样的:

代码语言:javascript
复制
void Error(const char *message);

测试宏是这样声明的:

代码语言:javascript
复制
#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0)

使用指针:

代码语言:javascript
复制
T* p = dynamic_cast<T *>(x);
ENSURE(p);
p->foo();

分析器将发出关于可能的空指针取消引用的警告,但代码实际上是安全的。如果指针为空,Error函数将抛出异常,从而阻止指针取消引用。

我们只需要通过使用函数注释方法之一来告诉分析器这一点,例如:

代码语言:javascript
复制
[[noreturn]] void Error(const char *message);

或者:

代码语言:javascript
复制
__declspec(noreturn) void Error(const char *message);

这将有助于消除错误警告。因此,正如您所看到的,在大多数情况下,使用您自己的宏来修复问题是相当容易的。

但是,如果您处理来自第三方库的粗心实现的宏,则可能会更加棘手。

这就引出了第三种类型的宏。你不能改变它们,分析器也无法弄清楚它们到底是如何工作的。这是一种常见的情况,因为宏可能以非常奇特的方式实现。

在这种情况下,有三个选项可供选择:

使用documentation;

  • use中描述的其中一种假阳性抑制方法(我在先前的answer;

  • email us.
  1. 中描述的技术)抑制警告。

我们正在逐渐增加对流行库中各种棘手宏的支持。事实上,分析器已经熟悉了您可能遇到的大多数特定宏,但是程序员的想象力是取之不尽的,我们只是不能预见每一个可能的实现。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47283349

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档