How to raise warning if return value is disregarded?

和自甴很熟 提交于 2019-12-17 04:26:04

问题


I'd like to see all the places in my code (C++) which disregard return value of a function. How can I do it - with gcc or static code analysis tool?

Bad code example:

int f(int z) {
    return z + (z*2) + z/3 + z*z + 23;
}


int main()
{
  int i = 7;
  f(i); ///// <<----- here I disregard the return value

  return 1;
}

Please note that:

  • it should work even if the function and its use are in different files
  • free static check tool

回答1:


You want GCC's warn_unused_result attribute:

#define WARN_UNUSED __attribute__((warn_unused_result))

int WARN_UNUSED f(int z) {
    return z + (z*2) + z/3 + z*z + 23;
}

int main()
{
  int i = 7;
  f(i); ///// <<----- here i disregard the return value
  return 1;
}

Trying to compile this code produces:

$ gcc test.c
test.c: In function `main':
test.c:16: warning: ignoring return value of `f', declared with
attribute warn_unused_result

You can see this in use in the Linux kernel; they have a __must_check macro that does the same thing; looks like you need GCC 3.4 or greater for this to work. Then you will find that macro used in kernel header files:

unsigned long __must_check copy_to_user(void __user *to,
                                        const void *from, unsigned long n);



回答2:


As far as I'm aware there is no GCC option to give this warning. However, if you are interested in specific functions, you can tag them with an attribute:

int fn() __attribute__((warn_unused_result));

which would give a warning if the return value of fn() was not used. Caveat: I've never used this feature myself.




回答3:


You can use this handy template to do it at run-time.

Instead of returning an error code (e.g. HRESULT) you return a return_code<HRESULT>, which asserts if it goes out of scope without the value being read. It's not a static analysis tool, but it's useful none the less.

class return_value
{
public:
  explicit return_value(T value)
    :value(value), checked(false)
  {
  }

  return_value(const return_value& other)
    :value(other.value), checked(other.checked)
  {
    other.checked = true;
  }

  return_value& operator=(const return_value& other)
  {
    if( this != &other ) 
    {
      assert(checked);
      value = other.value;
      checked = other.checked;
      other.checked = true;
    }
  }

  ~return_value(const return_value& other)
  {
    assert(checked);
  }

  T get_value()const {
    checked = true;
    return value;
  }

private:
  mutable bool checked;
  T value;
};



回答4:


For C++17 the answer to this question changes since we now have the [[nodiscard]] attribute. Covered in [dcl.attr.nodiscard]:

The attribute-token nodiscard may be applied to the declarator-id in a function declaration or to the declaration of a class or enumeration. It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.

and

[ Example:

struct [[nodiscard]] error_info { /* ... */ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
  enable_missile_safety_mode(); // warning encouraged
  launch_missiles();
}
error_info &foo();
void f() { foo(); }             // warning not encouraged: not a nodiscard call, because neither
                                // the (reference) return type nor the function is declared nodiscard

 — end example ]

So modifying your example (see it live):

[[nodiscard]] int f(int z) {
    return z + (z*2) + z/3 + z*z + 23;
}


int main()
{
  int i = 7;
  f(i); // now we obtain a diagnostic

  return 1;
}

We now obtain a diagnostic with both gcc and clang e.g.

warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
  f(i); // now we obtain a diagnostic
  ^ ~



回答5:


Any static analysis code (e.g. PC-Lint) should be able to tell you that. For PC-Lint, I know that this is the case.




回答6:


a static analyzer will do the work for you, but if your code base is more then trivial prepare to be overwhelmed ;-)




回答7:


A static analyzer will be your best bet here. We use Coverity here, but there are free tools available that you can use as well.

If you need a quick-and-dirty solution and you have a Linux-style shell handy, you can try something like:

grep -rn "function_name" * | grep -v "="

That will find every line that references the specified function but does not contain an "=". You can get a lot of false positives (and potentially some false negatives) but if you don't have a static analyzer it's a decent place to start.




回答8:


The classic 'lint' program used to be very voluble about functions that returned a value that was ignored. The trouble was, many of those warnings were unwanted - leading to excessive noise in the lint output (it was picking up bits of fluff that you wanted it to ignore). That's probably why GCC doesn't have a standard warning for it.

The other issue - the flip side - is "how do you suppress the warning when you know you are ignoring the result but really don't care". The classic scenario for that is:

if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    signal(SIGHUP, sighandler);

You care about the first result from signal(); you know that the second will be SIG_IGN (since you just set it to that). To get away from the warnings, I sometimes use some variant on:

if ((old = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
    old = signal(SIGHUP, sighandler);

This assigns to old both times. You can follow that with 'assert(old == SIG_IGN)'.



来源:https://stackoverflow.com/questions/2042780/how-to-raise-warning-if-return-value-is-disregarded

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!