Как вызвать предупреждение, если возвращаемое значение не учитывается? - PullRequest
50 голосов
/ 11 января 2010

Я хотел бы видеть все места в моем коде (C ++), которые игнорируют возвращаемое значение функции Как я могу это сделать - с помощью gcc или инструмента статического анализа кода?

Пример неверного кода:

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;
}

Обратите внимание, что:

  • должно работать, даже если функция и ее использование находятся в разных файлах
  • бесплатно инструмент статической проверки

Ответы [ 8 ]

54 голосов
/ 11 января 2010

Вы хотите атрибут warn_unused_result GCC:

#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;
}

Попытка скомпилировать этот код приводит к:

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

Вы можете увидеть это в использовании в ядре Linux ; у них есть макрос __must_check, который делает то же самое; похоже, вам нужен GCC 3.4 или выше, чтобы это работало. Затем вы найдете этот макрос, используемый в заголовочных файлах ядра:

unsigned long __must_check copy_to_user(void __user *to,
                                        const void *from, unsigned long n);
10 голосов
/ 11 января 2010

Вы можете использовать этот удобный шаблон, чтобы сделать это во время выполнения.

Вместо того, чтобы возвращать код ошибки (например, HRESULT), вы возвращаете код возврата , который подтверждает, выходит ли он из области видимости без считывания значения. Это не инструмент статического анализа, но, тем не менее, он полезен.

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;
};
10 голосов
/ 11 января 2010

Насколько я знаю, у GCC нет опции для выдачи этого предупреждения. Однако, если вас интересуют конкретные функции, вы можете пометить их атрибутом:

int fn() __attribute__((warn_unused_result));

, который выдает предупреждение, если возвращаемое значение fn () не используется. Предостережение: я никогда не использовал эту функцию сам.

8 голосов
/ 22 июля 2018

Для C ++ 17 ответ на этот вопрос меняется, поскольку теперь у нас есть атрибут [[nodiscard]] . Покрыто в [dcl.attr.nodiscard] :

Нодискард атрибут-токен может применяться к идентификатору объявления в объявлении функции или к объявлению класса или перечисления. Он должен появляться не более одного раза в каждом списке атрибутов, и не должно присутствовать ни одного атрибута-аргумента.

и

[Пример:

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

- конец примера]

Итак, измените ваш пример ( посмотрите вживую ):

[[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;
}

Теперь мы получаем диагностику с gcc и clang, например.

warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
  f(i); // now we obtain a diagnostic
  ^ ~
4 голосов
/ 11 января 2010

Статический анализатор будет вашим лучшим выбором здесь. Мы используем здесь Coverity, но есть бесплатных инструментов , которые вы также можете использовать.

Если вам нужно быстрое и грязное решение и у вас есть удобная оболочка в стиле Linux, вы можете попробовать что-то вроде:

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

Это найдет каждую строку, которая ссылается на указанную функцию, но не содержит "=". Вы можете получить много ложных срабатываний (и, возможно, некоторые ложные отрицания), но если у вас нет статического анализатора, это неплохое место для старта.

4 голосов
/ 11 января 2010

статический анализатор сделает всю работу за вас, но если ваша кодовая база более чем тривиальна, подготовьтесь к потрясению; -)

4 голосов
/ 11 января 2010

Любой код статического анализа (например, PC-Lint ) должен иметь возможность сообщить вам об этом. Для PC-Lint я знаю, что это так.

2 голосов
/ 11 января 2010

Классическая программа 'lint' была очень гибкой в ​​отношении функций, которые возвращали значение, которое было проигнорировано. Проблема заключалась в том, что многие из этих предупреждений были нежелательными, что приводило к чрезмерному шуму в выводе ворса (он собирал кусочки пуха, которые вы хотели игнорировать). Вероятно, поэтому в GCC нет стандартного предупреждения.

Другая проблема - обратная сторона - это «как вы подавляете предупреждение, когда знаете, что игнорируете результат, но на самом деле все равно». Классический сценарий для этого:

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

Вы заботитесь о первом результате из signal(); вы знаете, что второй будет SIG_IGN (так как вы просто установите его). Чтобы уйти от предупреждений, я иногда использую какой-то вариант на:

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

Это присваивает old оба раза. Вы можете следовать за этим с помощью 'assert (old == SIG_IGN)'.

...