Подтвердить что ты не робот

Как повысить предупреждение, если возвращаемое значение не учитывается?

Мне бы хотелось увидеть все места в моем коде (С++), которые не учитывают возвращаемое значение функции. Как я могу это сделать - с помощью 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;
}

Обратите внимание:

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

Ответ 1

Вам нужен атрибут GCC warn_unused_result:

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

Ответ 2

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

int fn() __attribute__((warn_unused_result));

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

Ответ 3

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

Вместо того, чтобы возвращать код ошибки (например, HRESULT), вы возвращаете return_code <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;
};

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

Классическая программа "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)".