我正在使用PVS-Studio来分析我的测试代码。通常存在以下形式的构造:
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风格进行检查,也会发生这种情况。
发布于 2017-11-15 21:02:42
感谢您的有趣示例。我们将思考,我们可以对BOOST_REQUIRE宏做些什么。
目前,我可以建议您以下解决方案:
之后的某处
#include <boost/test/included/unit_test.hpp>你可以这样写:
#ifdef PVS_STUDIO
#undef BOOST_REQUIRE
#define BOOST_REQUIRE(expr) do { if (!(expr)) throw "PVS-Studio"; } while (0)
#endif这样,您将向分析器发出一个提示,即false条件会导致控制流中止。这不是最好的解决方案,但我认为它值得告诉你。
发布于 2017-11-16 16:49:23
回复一个大的评论不是一个好主意,所以下面是我对以下主题的详细回应:
虽然这是可能的,但在所有测试用例文件中包含定义将是一件痛苦的事情。此外,这不仅限于BOOST_REQUIRE,还适用于assert、SDL_Assert或用户可能使用的任何其他自定义宏。
人们应该理解,有三种类型的测试宏,每一种都应该单独讨论。
第一种类型的宏只是警告您在Debug版本中出现了错误。一个典型的例子是assert宏。以下代码将导致PVS-Studio分析器生成警告:
T* p = dynamic_cast<T *>(x);
assert(p);
p->foo();分析器将在此处指出可能的空指针取消引用,并将是正确的。使用assert的检查是不够的,因为它将从发布版本中删除。也就是说,结果是没有支票。实现它的一种更好的方法是将代码重写为如下所示:
T* p = dynamic_cast<T *>(x);
if (p == nullptr)
{
assert(false);
throw Error;
}
p->foo();此代码不会触发警告。
您可能会争辩说,您100%确定dynamic_cast永远不会返回nullptr。我不接受这个论点。如果您完全确定强制转换总是正确的,那么您应该使用速度更快的static_cast。如果您不确定,则必须在取消引用指针之前对其进行测试。
好吧,好吧,我明白你的意思。您确信代码是正确的,但您需要使用dynamic_cast进行检查,以防万一。好的,然后使用下面的代码:
assert(dynamic_cast<T *>(x) != nullptr);
T* p = static_cast<T *>(x);
p->foo();我不喜欢它,但至少它更快,因为在发布版本中将忽略较慢的dynamic_cast操作符,而分析器将保持沉默。
继续讨论下一种类型的宏。
第二种类型的宏只是警告您在Debug版本中出现了错误,并在测试中使用。它们与前一种类型的不同之处在于,如果条件为false,则它们会停止测试中的算法,并生成错误消息。
这些宏的基本问题是函数没有被标记为不返回。下面是一个例子。
假设我们有一个通过抛出异常来生成错误消息的函数。它的声明是这样的:
void Error(const char *message);测试宏是这样声明的:
#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0)使用指针:
T* p = dynamic_cast<T *>(x);
ENSURE(p);
p->foo();分析器将发出关于可能的空指针取消引用的警告,但代码实际上是安全的。如果指针为空,Error函数将抛出异常,从而阻止指针取消引用。
我们只需要通过使用函数注释方法之一来告诉分析器这一点,例如:
[[noreturn]] void Error(const char *message);或者:
__declspec(noreturn) void Error(const char *message);这将有助于消除错误警告。因此,正如您所看到的,在大多数情况下,使用您自己的宏来修复问题是相当容易的。
但是,如果您处理来自第三方库的粗心实现的宏,则可能会更加棘手。
这就引出了第三种类型的宏。你不能改变它们,分析器也无法弄清楚它们到底是如何工作的。这是一种常见的情况,因为宏可能以非常奇特的方式实现。
在这种情况下,有三个选项可供选择:
我们正在逐渐增加对流行库中各种棘手宏的支持。事实上,分析器已经熟悉了您可能遇到的大多数特定宏,但是程序员的想象力是取之不尽的,我们只是不能预见每一个可能的实现。
https://stackoverflow.com/questions/47283349
复制相似问题