Как отключить предупреждения GCC для нескольких строк кода

220

В Visual C ++ это можно использовать #pragma warning (disable: ...). Также я обнаружил, что в GCC вы можете переопределить флаги компилятора файлов . Как я могу сделать это для "следующей строки", или с семантикой push / pop вокруг областей кода, используя GCC?

Мэтт Джойнер
источник
1
возможный дубликат отключения определенных предупреждений в gcc - Упс, на самом деле этот вопрос сам по себе является обманом (но не закрытым). Это как раз тот, который появился в разделе «Связанные». Во всяком случае, это было задано несколько раз на SO.
Тайлер МакГенри
1
@paxdiablo: я делаю наоборот. Я поднял уровень предупреждения очень высоко и хочу построчно подавлять предупреждения, чтобы убедиться, что все в порядке.
Мэтт Столяр
4
@Tyler McHenry: Если вы проверили более внимательно, вы можете заметить, что связанный вопрос содержит решение для каждого файла, именно то, которое я упомянул в своем собственном вопросе как неудовлетворительное (я даже украл ссылку).
Мэтт Джойнер
6
@paxdiablo, компиляторы дают ложные срабатывания, иногда вы хотите скомпилировать с -Werror, но эти ложные срабатывания не блокируют сборку. поэтому отключение специальных случаев и комментирование почему - имеет смысл в некоторых случаях. Есть и другие случаи, когда это может быть удобно - например, автоматическая генерация кода, который выдает безобидные предупреждения, которые не так просто ввести и изменить (так как код генерируется), хотя в этом случае отключение для каждого файла с большей вероятностью будет решение.
ideasman42

Ответы:

221

Похоже, это можно сделать . Я не могу определить версию GCC, в которую он был добавлен, но это было где-то до июня 2010 года.

Вот пример:

#pragma GCC diagnostic error "-Wuninitialized"
    foo(a);         /* error is given for this one */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
    foo(b);         /* no diagnostic for this one */
#pragma GCC diagnostic pop
    foo(c);         /* error is given for this one */
#pragma GCC diagnostic pop
    foo(d);         /* depends on command line options */
Мэтт Джойнер
источник
14
один pushи два popс - может быть другого pushв начале не хватает?
бездна.7
37
"#pragma Диагностическое нажатие GCC #pragma Диагностическое всплывающее окно GCC Заставляет GCC запоминать состояние диагностики при каждом нажатии и восстанавливать эту точку в каждом всплывающем окне. Если у всплывающего окна нет совпадающего push, параметры командной строки восстанавливаются. " - из руководства GCC: gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html
бобпаул
11
Для справки, gcc версии 4.4.3 поддерживает ошибку / предупреждение / игнорируется, но не push / pop
frankster
12
Первая версия GCC, которая имела диагностический push / pop, - это GCC 4.6.4 . Я определил это, просмотрев раздел Diagnostic-Pragmas.html # Diagnostic-Pragmas для каждой версии GCC в документации GCC
bitek
5
Это позор, это не работает на практике. В некоторых случаях выдает больше предупреждений. Или, вернее, на практике это не работает для GCC с 4.7 по 5.1. См., Например, GCC не соблюдает «прагма GCC диагностики», чтобы заставить замолчать предупреждения .
jww
108

Чтобы вывести все из строя, это пример временного отключения предупреждения:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    write(foo, bar, baz);
#pragma GCC diagnostic pop

Вы можете проверить документацию GCC о диагностических прагмах для более подробной информации.

Ян Пилчер
источник
2
Должно работать, но мой gcc-4.9просто игнорирует эту строку полностью.
Алексей Петренко,
31

TL; DR : если это работает, избегайте или используйте спецификаторы как __attribute__, иначе _Pragma.

Это короткая версия моей статьи в блоге « Подавление предупреждений в GCC и Clang» .

Рассмотрим следующее Makefile

CPPFLAGS:=-std=c11 -W -Wall -pedantic -Werror

.PHONY: all
all: puts

для построения следующего puts.cисходного кода

#include <stdio.h>

int main(int argc, const char *argv[])
{
    while (*++argv) puts(*argv);
    return 0;
}

Он не скомпилируется, потому что argcон не используется, а настройки хардкорные ( -W -Wall -pedantic -Werror).

Есть 5 вещей, которые вы можете сделать:

  • Улучшите исходный код, если это возможно
  • Используйте спецификатор объявления, например __attribute__
  • использование _Pragma
  • использование #pragma
  • Используйте параметр командной строки.

Улучшение источника

Первой попыткой должна быть проверка возможности улучшения исходного кода, чтобы избавиться от предупреждения. В этом случае мы не хотим менять алгоритм только из-за этого, так как argcон избыточен !*argv( NULLпосле последнего элемента).

Использование спецификатора объявления, например __attribute__

#include <stdio.h>

int main(__attribute__((unused)) int argc, const char *argv[])
{
    while (*++argv) puts(*argv);
    return 0;
}

Если вам повезет, стандарт предоставляет спецификатор для вашей ситуации, например _Noreturn.

__attribute__является проприетарным расширением GCC (также поддерживается Clang и некоторыми другими компиляторами armcc) и не будет понято многими другими компиляторами. Поместите __attribute__((unused))в макрос, если вы хотите переносимый код.

_Pragma оператор

_Pragmaможет быть использован в качестве альтернативы #pragma.

#include <stdio.h>

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")

int main(int argc, const char *argv[])
{
    while (*++argv) puts(*argv);
    return 0;
}
_Pragma("GCC diagnostic pop")

Основное преимущество _Pragmaоператора заключается в том, что вы можете поместить его в макросы, что невозможно с помощью #pragmaдирективы.

Недостаток: это почти тактическая ядерная бомба, поскольку она работает на основе строк, а не на основе объявлений.

_PragmaОператор был введен в C99.

#pragma директивы.

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

#include <stdio.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
int main(int argc, const char *argv[])
{
    while (*++argc) puts(*argv);
    return 0;
}
#pragma GCC diagnostic pop

Недостаток: это почти тактическая ядерная бомба, поскольку она работает на основе строк, а не на основе объявлений.

Обратите внимание, что подобный синтаксис существует в Clang .

Подавление предупреждения в командной строке для одного файла

Мы могли бы добавить в строку следующую строку, Makefileчтобы отключить предупреждение специально для пут:

CPPFLAGS:=-std=c11 -W -Wall -pedantic -Werror

.PHONY: all
all: puts

puts.o: CPPFLAGS+=-Wno-unused-parameter

Это, вероятно, не хочет, чтобы вы хотели в вашем конкретном случае, но это может помочь другим чтения, которые находятся в подобных ситуациях.

Кристиан Худжер
источник
2
re: improving the sourceэто также сработает, чтобы изменить объявление main int main(int, const char* argv[]) { ... }, не задавая аргументу имя, вы сообщаете компилятору, что он не будет использоваться.
Джесси Чизхолм
1
@JesseChisholm опускание имени параметра в определении функции невозможно. См 6.9.1 Функциональные definintions ИСО / IEC9899, § 5 «Если описатель содержит список типов параметров, декларация каждого параметра должна включать в себя идентификатор [...]» И правильно , так что код будет отвергнут gcc, а также clang.
Кристиан Худжер
1
Другой способ - просто выполнить приведение переменной к void. На самом деле, я видел в проекте следующий макрос: #define UNUSED(x) ((void)x)используется для отключения предупреждений. Я думаю, что это было в ReactOS?
Пол Стелиан
1
Я не думаю, что после этого вам нужен обратный слеш, нет? _Pragma("GCC diagnostic pop") \ должно быть, _Pragma("GCC diagnostic pop")я думаю.
Габриэль Стейплс
1
@GabrielStaples Это правильно, спасибо, что заметили, я обновил ответ.
Кристиан Худжер
20
#define DIAG_STR(s) #s
#define DIAG_JOINSTR(x,y) DIAG_STR(x ## y)
#ifdef _MSC_VER
#define DIAG_DO_PRAGMA(x) __pragma (#x)
#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(warning(x))
#else
#define DIAG_DO_PRAGMA(x) _Pragma (#x)
#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(compiler diagnostic x)
#endif
#if defined(__clang__)
# define DISABLE_WARNING(gcc_unused,clang_option,msvc_unused) DIAG_PRAGMA(clang,push) DIAG_PRAGMA(clang,ignored DIAG_JOINSTR(-W,clang_option))
# define ENABLE_WARNING(gcc_unused,clang_option,msvc_unused) DIAG_PRAGMA(clang,pop)
#elif defined(_MSC_VER)
# define DISABLE_WARNING(gcc_unused,clang_unused,msvc_errorcode) DIAG_PRAGMA(msvc,push) DIAG_DO_PRAGMA(warning(disable:##msvc_errorcode))
# define ENABLE_WARNING(gcc_unused,clang_unused,msvc_errorcode) DIAG_PRAGMA(msvc,pop)
#elif defined(__GNUC__)
#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,push) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option))
# define ENABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,pop)
#else
# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option))
# define ENABLE_WARNING(gcc_option,clang_option,msvc_unused) DIAG_PRAGMA(GCC,warning DIAG_JOINSTR(-W,gcc_option))
#endif
#endif

Это должно сделать трюк для gcc, clang и msvc

Может быть вызван с помощью, например:

DISABLE_WARNING(unused-variable,unused-variable,42)
[.... some code with warnings in here ....]
ENABLE_WARNING(unused-variable,unused-variable,42)

см. https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html , http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas и https://msdn.microsoft. .com / de-DE / library / d9x1s805.aspx для более подробной информации

Вам нужна по крайней мере версия 4.02, чтобы использовать эти виды прагм для gcc, не уверен насчет msvc и clang по поводу версий.

Похоже, что прагм-прагма для gcc немного сломана. Если вы снова включите предупреждение, вы все равно получите предупреждение для блока, который был внутри блока DISABLE_WARNING / ENABLE_WARNING. Для некоторых версий gcc это работает, для некоторых - нет.

Мартин Герхарди
источник
3
Ты настоящий MVP
zeboidlund
19
#pragma GCC diagnostic ignored "-Wformat"

Замените «-Wformat» на имя вашего флага предупреждения.

AFAIK нет способа использовать семантику push / pop для этой опции.

Джо Д
источник
4
Это позор, это не работает на практике. В некоторых случаях выдает больше предупреждений. Или, вернее, на практике это не работает для GCC с 4.7 по 5.1. См., Например, GCC не соблюдает «прагма GCC диагностики», чтобы заставить замолчать предупреждения .
15:15
6

У меня была такая же проблема с внешними библиотеками, такими как заголовки ROS. Мне нравится использовать следующие параметры в CMakeLists.txt для более строгой компиляции:

set(CMAKE_CXX_FLAGS "-std=c++0x -Wall -Wextra -Wstrict-aliasing -pedantic -Werror -Wunreachable-code ${CMAKE_CXX_FLAGS}")

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

//save compiler switches
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

//Bad headers with problem goes here
#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>

//restore compiler switches
#pragma GCC diagnostic pop
Шиталь шах
источник
2
Разве это лучше не обрабатывать с помощью системных каталогов gcc ?
Красный XIII
@RedXIII - да, это вариант, если вы можете составить список таких каталогов и указать в командной строке gcc. Однако много раз компилятор вызывается глубоко в конвейере, или вы не имеете большого контроля над тем, как кто-то другой должен компилировать ваш код. В этих случаях выше, вероятно, лучшее решение.
Шиталь Шах
5

Я знаю, что вопрос о GCC, но для людей, которые ищут, как это сделать в других и / или нескольких компиляторах ...

TL; DR

Возможно, вы захотите взглянуть на Hedley , который является одним из общедоступных заголовков C / C ++, который я написал и который делает для вас многое из этого. Я помещу краткий раздел о том, как использовать Хедли для всего этого в конце этого поста.

Отключение предупреждения

#pragma warning (disable: …) имеет эквиваленты в большинстве компиляторов:

  • MSVC: #pragma warning(disable:4996)
  • GCC: #pragma GCC diagnostic ignored "-W…"где многоточие - это название предупреждения; например , #pragma GCC diagnostic ignored "-Wdeprecated-declarations.
  • лязг: #pragma clang diagnostic ignored "-W…". Синтаксис в основном такой же, как в GCC, и многие имена предупреждений совпадают (хотя многие не совпадают).
  • Компилятор Intel C: используйте синтаксис MSVC, но имейте в виду, что номера предупреждений совершенно разные. Пример: #pragma warning(disable:1478 1786).
  • ЗГУ: Есть diag_suppressпрагма:#pragma diag_suppress 1215,1444
  • TI: есть diag_suppressпрагма с тем же синтаксисом (но с разными номерами предупреждений!), Что и с PGI:pragma diag_suppress 1291,1718
  • Oracle Developer Studio (suncc): есть error_messagesпрагма. Досадно, что предупреждения для компиляторов C и C ++ различны. Оба из них отключают в основном одни и те же предупреждения:
    • C: #pragma error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)
    • C ++: #pragma error_messages(off,symdeprecated,symdeprecated2)
  • IAR: также используется diag_suppressкак PGI и TI, но синтаксис другой. Некоторые номера предупреждений одинаковы, но другие разошлись:#pragma diag_suppress=Pe1444,Pe1215
  • Pelles C: похож на MSVC, хотя опять-таки цифры разные #pragma warn(disable:2241)

Для большинства компиляторов часто хорошей идеей является проверка версии компилятора, прежде чем пытаться отключить ее, в противном случае вы просто получите другое предупреждение. Например, в GCC 7 добавлена ​​поддержка -Wimplicit-fallthroughпредупреждения, поэтому, если вы заботитесь о GCC до 7, вы должны сделать что-то вроде

#if defined(__GNUC__) && (__GNUC__ >= 7)
#  pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

Для clang и компиляторов, основанных на clang, таких как более новые версии XL C / C ++ и armclang, вы можете проверить, знает ли компилятор о конкретном предупреждении с помощью __has_warning()макроса.

#if __has_warning("-Wimplicit-fallthrough")
#  pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif

Конечно, вы также должны проверить, __has_warning()существует ли макрос:

#if defined(__has_warning)
#  if __has_warning("-Wimplicit-fallthrough")
#    pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#  endif
#endif

Вы можете испытать желание сделать что-то вроде

#if !defined(__has_warning)
#  define __has_warning(warning)
#endif

Таким образом, вы можете использовать __has_warningнемного проще. Clang даже предлагает нечто подобное для __has_builtin()макроса в их руководстве. Не делай этого . Другой код может проверять __has_warningи возвращаться к проверке версий компилятора, если он не существует, и если вы определите, __has_warningвы нарушите их код. Правильный способ сделать это - создать макрос в вашем пространстве имен. Например:

#if defined(__has_warning)
#  define MY_HAS_WARNING(warning) __has_warning(warning)
#else
#  define MY_HAS_WARNING(warning) (0)
#endif

Тогда вы можете делать такие вещи, как

#if MY_HAS_WARNING(warning)
#  pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#elif defined(__GNUC__) && (__GNUC__ >= 7)
#  pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

Нажатие и сование

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

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
call_deprecated_function();
#pragma GCC diagnostic pop

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

  • GCC 4.6+: #pragma GCC diagnostic push/#pragma GCC diagnostic pop
  • лязг: #pragma clang diagnostic push/#pragma diagnostic pop
  • Intel 13+ (а возможно и раньше): #pragma warning(push)/#pragma warning(pop)
  • MSVC 15+ (VS 9.0 / 2008): #pragma warning(push)/#pragma warning(pop)
  • ARM 5.6+: #pragma push/#pragma pop
  • TI 8.1+: #pragma diag_push/#pragma diag_pop
  • Pelles C 2.90+ (а возможно и раньше): #pragma warning(push)/#pragma warning(pop)

Если память служит, для некоторых очень старых версий GCC (таких как 3.x, IIRC) прагмы push / pop должны быть вне функции.

Сокрытие кровавых деталей

Для большинства компиляторов можно скрыть логику использования макросов _Pragma, которая была представлена ​​в C99. Даже в режиме без C99 большинство компиляторов поддерживают _Pragma; большое исключение - MSVC, у которого есть собственное __pragmaключевое слово с другим синтаксисом. Стандарт _Pragmaпринимает строку, версия Microsoft не:

#if defined(_MSC_VER)
#  define PRAGMA_FOO __pragma(foo)
#else
#  define PRAGMA_FOO _Pragma("foo")
#endif
PRAGMA_FOO

Примерно эквивалентно, после предварительной обработки,

#pragma foo

Это позволяет нам создавать макросы, чтобы мы могли писать код

MY_DIAGNOSTIC_PUSH
MY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated_function();
MY_DIAGNOSTIC_POP

И скрыть все уродливые проверки версий в определениях макросов.

Простой способ: Хедли

Теперь, когда вы понимаете механику того, как делать подобные вещи, сохраняя при этом чистый код, вы понимаете, что делает один из моих проектов, Хедли . Вместо того, чтобы копаться в тоннах документации и / или устанавливать столько версий компиляторов, сколько вы можете протестировать, вы можете просто включить Hedley (это единственный общедоступный заголовок C / C ++) и покончить с этим. Например:

#include "hedley.h"

HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated();
HEDLEY_DIAGNOSTIC_POP

Отключит предупреждение о вызове устаревшей функции в GCC, clang, ICC, PGI, MSVC, TI, IAR, ODS, Pelles и, возможно, других (возможно, я не буду обновлять этот ответ, когда обновляю Хедли). И на компиляторах, которые, как известно, не работают, макросы будут предварительно обработаны, и ваш код продолжит работать с любым компилятором. Конечно, HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATEDэто не единственное предупреждение, о котором знает Хедли, и не отключение предупреждений, которые может сделать Хедли, но, надеюсь, вы поняли идею.

nemequ
источник
3

Вместо того, чтобы заглушать предупреждения, стиль gcc обычно использует либо стандартные конструкции C, либо __attribute__расширение, чтобы сообщить компилятору больше о вашем намерении. Например, предупреждение о назначении, используемом в качестве условия, подавляется путем помещения назначения в круглые скобки, то есть if ((p=malloc(cnt)))вместо if (p=malloc(cnt)). Предупреждения о неиспользуемых аргументах функции могут быть подавлены каким-то странным, который __attribute__я никогда не смогу запомнить, или самовозвратом и т. Д. Но обычно я предпочитаю просто глобально отключить любую опцию предупреждения, которая генерирует предупреждения для вещей, которые будут происходить в правильном коде.

R .. GitHub СТОП, ПОМОГАЯ ЛЬДУ
источник
2
Может быть, так. Мое намерение состоит не в том, чтобы доказать какую-либо общую модель случая, а скорее в том, как выглядит философия gcc по предупреждению предупреждений.
R .. GitHub ОСТАНОВИТЬ, ПОМОГАЯ ЛЬДУ
Компилятор ведет себя по-разному с предупреждениями W / R / T с добавлением скобок?!?! ?? !!!! ВОТ ЭТО ДА! Это неожиданно.
Джейсон С
1
@JasonS Паренс не меняет поведение компилятора в отношении предупреждений, он меняет семантику оператора. Дополнительные символы заставляют компилятор завершать присваивание и сохраняют его окончательное значение как выражение, которое не заслуживает предупреждения. Если вы хотите ясности, вы можете сказать if ((p=malloc(cnt)) != NULL) ..., что именно это и делает закулисный компилятор.
Джесси Чизхольм
@JesseChisholm: я не думаю, что ваше объяснение является точным.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
3

Для тех, кто нашел эту страницу, ищет способ сделать это в IAR, попробуйте это:

#pragma diag_suppress=Pe177
void foo1( void )
{
   /* The following line of code would normally provoke diagnostic 
      message #177-D: variable "x" was declared but never referenced.
      Instead, we have suppressed this warning throughout the entire 
      scope of foo1(). 
   */
   int x;
}
#pragma diag_default=Pe177

См. Http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472m/chr1359124244797.html для справки.

Keron
источник