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