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

StackOverflow https://stackoverflow.com/questions/2042780

  •  19-09-2019
  •  | 
  •  

Вопрос

Я хотел бы увидеть все места в моем коде (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;
}

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

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

Решение

Вы хотите 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);

Другие советы

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

int fn() __attribute__((warn_unused_result));

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

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

Вместо возврата кода ошибки (например.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;
};

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

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
  ^ ~

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top