简体   繁体   English

有没有办法帮助我 Clang Static Analyzer 理解全局常量对象?

[英]Is there a way for me to help Clang Static Analyzer understand global constant objects?

Background: because reasons , my code likes to return success/error-code values from its functions in the form of a (very lightweight) class-object.背景:由于原因,我的代码喜欢以(非常轻量级的)类对象的形式从其函数返回成功/错误代码值。 This works fine, however I'm having some difficulty getting Clang Static Analyzer to understand what's going on;这工作正常,但是我很难让 Clang Static 分析器了解发生了什么; its confusion causes it to emit spurious "uninitialized-value" warnings when it analyzes my program.它的混乱导致它在分析我的程序时发出虚假的“未初始化值”警告。

Here's some minimal example code to illustrate/reproduce the (mis)behavior:这里有一些最小的示例代码来说明/重现(错误)行为:

// my_status_t_class.h // my_status_t_class.h

#ifndef MY_STATUS_T_CLASS_H
#define MY_STATUS_T_CLASS_H

class status_t
{
public:
   status_t(int errorNumber) : _errorNumber(errorNumber) {/* empty */}

   bool IsError() const {return (_errorNumber != 0);}

private:
   int _errorNumber;
};

// A list of some common error codes
const status_t STATUS_NO_ERROR(0);
const status_t STATUS_DATA_NOT_FOUND(-1);
// [...]

#endif

// my_test_program.cpp // my_test_program.cpp

#include <stdio.h>
#include <stdlib.h>

#include "my_status_t_class.h"

status_t GetValue(int & ret) 
{
   if (rand()%2)
   {
      ret = 5;
      return STATUS_NO_ERROR;
   }
   else return STATUS_DATA_NOT_FOUND;
}

int main(int argc, char ** argv)
{
   int ret;
   if (GetValue(ret).IsError()) ret = 5;
   printf("ret=%i\n", ret);
   return 0;
}

When I run scan-build on the above code, it gives this output:当我在上面的代码上运行scan-build时,它给出了这个 output:

$ scan-build g++ -std=c++17 my_test_program.cpp
scan-build: Using '/Users/jaf/llvm-project/build/bin/clang-16' for static analysis
testhashtable.cpp:20:8: warning: 2nd function call argument is an uninitialized value [core.CallAndMessage]
   printf("ret=%i\n", ret);
   ^~~~~~~~~~~~~~~~~~~~~~~

1 warning generated.生成 1 个警告。

.... and it generates a report about the error which can be viewed at this link . .... 它会生成有关错误的报告,可以在此链接中查看。

As you can see from the report, ClangSA seems to think that it is possble for my GetValue() function to return STATUS_DATA_NOT_FOUND , but then have the IsError() method return false inside main() , leaving the ret local-variable undefined when it is used.正如您从报告中看到的那样,ClangSA 似乎认为我的GetValue() function 有可能返回STATUS_DATA_NOT_FOUND ,但随后IsError()方法在main()内部返回 false,当ret局部变量未定义时用来。

In actuality, that isn't possible -- IsError() will return true, so then main() will execute ret = 5;实际上,这是不可能的IsError()将返回 true,因此main()将执行ret = 5; and so ret will always have a well-defined value when I pass it in to printf() .因此,当我将ret传递给printf()时,它始终具有明确定义的值。

My question is, is this a bug in ClangSA?我的问题是,这是 ClangSA 中的错误吗? Or if not, is there a recommended way for me to get ClangSA to understand/follow the semantics of my status_t class here?或者,如果没有,是否有推荐的方法让我在此处让 ClangSA 理解/遵循我的status_t class 的语义? (It seems like it should be able to, since the code is all defined in the my_status_t_class.h header file which ClangSA has direct access to). (看起来应该可以,因为代码都在my_status_t_class.h header 文件中定义,ClangSA 可以直接访问该文件)。

I did notice that if I replace the constant-declarations in my_status_t_class.h with #define declarations instead, I get the behavior I want, eg if I do this:我确实注意到,如果我用#define声明替换my_status_t_class.h中的常量声明,我会得到我想要的行为,例如,如果我这样做:

// my_status_t_class.h // my_status_t_class.h

[...]

// A list of some common error codes
#define STATUS_NO_ERROR       status_t(0)
#define STATUS_DATA_NOT_FOUND status_t(-1)
// [...]

... then ClangSA doesn't produce the spurious warning... but of course that is quite an ugly solution, and I'd like to avoid it if there is a better alternative. ...然后 ClangSA 不会产生虚假警告...但这当然是一个非常丑陋的解决方案,如果有更好的选择,我想避免使用它。

I think the problem is that the analyzer is not able to see that the STATUS_DATA_NOT_FOUND object is always an error, so it can't be sure that ret will always be initialized.我认为问题在于分析器无法看到STATUS_DATA_NOT_FOUND object 始终是一个错误,因此不能确定ret将始终被初始化。

The reason it can't see that is because the STATUS_DATA_NOT_FOUND object is not a constant expression, so the analyzer can't know its value at compile-time.它看不到的原因是因为STATUS_DATA_NOT_FOUND object 不是常量表达式,所以分析器无法在编译时知道它的值。

One way to fix this would be to make the STATUS_DATA_NOT_FOUND object a constant expression, by using a constexpr constructor:解决此问题的一种方法是使用constexpr构造函数使STATUS_DATA_NOT_FOUND object 成为常量表达式:


class status_t
{
public:
   constexpr status_t(int errorNumber) : _errorNumber(errorNumber) {/* empty */}


constexpr status_t STATUS_DATA_NOT_FOUND(-1);

With that change, the analyzer should be able to see that STATUS_DATA_NOT_FOUND is always an error, and so it will know that ret will always be initialized.通过该更改,分析器应该能够看到STATUS_DATA_NOT_FOUND始终是一个错误,因此它将知道ret将始终被初始化。

Alternatively, you could make the STATUS_DATA_NOT_FOUND object a global variable instead of a constant object, and initialize it with a constant expression:或者,您可以使STATUS_DATA_NOT_FOUND object 成为全局变量而不是常量 object,并使用常量表达式对其进行初始化:


status_t STATUS_DATA_NOT_FOUND = status_t(-1);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM