C++ 在PVS Studio中BOOST_REQUIRE之后将变量标记为非NULL

C++ 在PVS Studio中BOOST_REQUIRE之后将变量标记为非NULL,c++,null-pointer,pvs-studio,boost.test,C++,Null Pointer,Pvs Studio,Boost.test,我正在使用PVS Studio分析我的测试代码。通常存在形式的构造 const noAnimal* animal = dynamic_cast<noAnimal*>(...); BOOST_REQUIRE(animal); BOOST_REQUIRE_EQUAL(animal->GetSpecies(), ...); const noAnimal*animal=dynamic_cast(…); 助推器(动物); 增强需要相等(动物->获取物种(),…); 但是,我仍然收到一

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

const noAnimal* animal = dynamic_cast<noAnimal*>(...);
BOOST_REQUIRE(animal);
BOOST_REQUIRE_EQUAL(animal->GetSpecies(), ...);
const noAnimal*animal=dynamic_cast(…);
助推器(动物);
增强需要相等(动物->获取物种(),…);
但是,我仍然收到一条警告,最后一行可能会取消对潜在空指针“animal”的引用

我知道可以将函数标记为“不返回NULL”,但也可以将函数标记为有效的NULL检查,或者让PVS Studio以其他方式意识到
animal
BOOST\u REQUIRE(animal)之后不能为NULL


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

感谢您提供了一个有趣的示例。我们会想,我们可以用
BOOST\u REQUIRE
宏做些什么

目前,我可以为您提供以下解决方案:

之后某处

#include <boost/test/included/unit_test.hpp>
这样,您将向分析器发出一个提示,假条件导致控制流中止。
这不是最漂亮的解决方案,但我认为它值得告诉你。

用一个大的评论回应是一个坏主意,因此下面是我对以下主题的详细回应:

虽然这是可能的,但将该定义包含在 所有测试用例文件。此外,这不仅限于BOOST_所需,还包括 也适用于assert、SDL_assert或用户指定的任何其他自定义宏 可能需要

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

第一种类型的宏只是警告您调试版本出错。典型的例子是
assert
宏。以下代码将导致PVS Studio analyzer生成警告:

T* p = dynamic_cast<T *>(x);
assert(p);
p->foo();
此代码不会触发警告

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

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

assert(dynamic_cast<T *>(x) != nullptr);
T* p = static_cast<T *>(x);
p->foo();
下面是测试宏的声明方式:

#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0)
使用指针:

T* p = dynamic_cast<T *>(x);
ENSURE(p);
p->foo();
或:

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

但是,如果处理来自第三方库的不小心实现的宏,可能会更棘手

这就引出了第三类宏。您无法更改它们,分析器也无法确定它们的具体工作方式。这是一种常见的情况,因为宏可能以非常奇特的方式实现

在这种情况下,您还有三个选择:

  • 使用本发明中描述的假阳性抑制手段之一抑制警告
  • 使用我在前面介绍的技术
  • 给我们发电子邮件

  • 我们正在逐渐增加对流行库中各种复杂宏的支持。事实上,分析器已经熟悉您可能遇到的大多数特定宏,但程序员的想象力是无穷的,我们无法预见每一个可能的实现。

    谢谢您的回复。尽管这是可能的,但是在所有的测试用例文件中包含这个定义将是一件痛苦的事情。此外,这不仅限于
    BOOST\u REQUIRE
    ,还适用于
    assert
    SDL\u assert
    或用户可能使用的任何其他自定义宏。因此,我看到了两种解决方案:要么让用户指定一个“assert”(宏名称)列表,然后假设条件为true(因为这是assert的原因)。要么至少有一个全局的、只有PVS的文件,其中的定义由PVS读取并假设为使用(类似于
    cpp.hint
    )。所以你可以把这样的定义放在一个中心位置。然而,我认为这将是困难的,因为定义可能会在任何文件包括。我不同意你的动态_演员故事。首先,有时您需要动态转换(多重继承…),但请确保它不会失败(例如,虚拟函数返回类型)。或者第二,您断言它是有效的,但仍然保持动态_强制转换,以便在发布模式下获得空指针异常,而不是UD。在我看来,这比对(应该是)不可能的情况进行额外的检查和抛出要好。(例如,一个函数返回了该类型,因此它应该是可转换的,但您希望防止该函数中出现C&P错误。棘手的宏:这就是我建议配置选项的原因
    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();
    
    [[noreturn]] void Error(const char *message);
    
    __declspec(noreturn) void Error(const char *message);