Флаги предупреждения Clang для разработки Objective-C

86

Как программист C & Objective-C, я немного параноик с флагами предупреждения компилятора.
Я обычно пытаюсь найти полный список флагов предупреждений для используемого мной компилятора и включать большинство из них, если только у меня нет веской причины не включать его.

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

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

Что касается субъективной части этого вопроса, мне интересно услышать об этой теме других разработчиков (в основном C, Objective-C и C ++).
Вы действительно заботитесь о таких вещах, как педантичные предупреждения и т. Д.? И если да или нет, то почему?

Теперь о Objective-C, я недавно полностью переключился на набор инструментов LLVM (с Clang) вместо GCC.

В моем производственном коде я обычно устанавливаю эти флаги предупреждения (явно, даже если некоторые из них могут быть охвачены -Wall):

  • -Wall
  • -Wbad-функция-литой
  • -Wcast выравнивания
  • -Wconversion
  • -Wdeclaration-после заявления
  • -Wdeprecated-реализации
  • -Wextra
  • -Wfloat равных
  • -Wformat = 2
  • -Wformat-nonliteral
  • -Wfour-символьная-константа
  • -Wimplicit-атомно-свойства
  • -Wmissing-распорки
  • -Wmissing-декларации
  • -Wmissing-полевые инициализаторы
  • -Wmissing-формат-атрибут
  • -Wmissing-noreturn
  • -Wmissing-прототипы
  • -Wnested-экстернов
  • -Wnewline-ВФ
  • -Wold стиль четкость
  • -Woverlength-строки
  • -Wparentheses
  • -Wpointer-Arith
  • -Wredundant-decls
  • -Wreturn типа
  • -Wsequence-точка
  • -Wshadow
  • -Wshorten-64-к-32
  • -Wsign-сравнить
  • -Wsign-преобразования
  • -Wstrict-прототипы
  • -Wstrict-селектор матча
  • -Wswitch
  • -Wswitch по умолчанию
  • -Wswitch-перечисление
  • -Wundeclared-селектор
  • -Wuninitialized
  • -Wunknown-псевдокомментарий
  • -Wunreachable-код
  • -Wunused-функция
  • -Wunused метки
  • -Wunused-параметр
  • -Wunused значение
  • -Wunused переменной
  • -Wwrite-строки

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

Например, вы думаете, что я пропустил определенный флаг для Clang (Objective-C), и почему?
Или вы думаете, что конкретный флаг бесполезен (или вообще не нужен) и почему?

РЕДАКТИРОВАТЬ

Чтобы прояснить вопрос, обратите внимание, что -Wallсодержит только несколько основных предупреждений.
На самом деле они представляют собой гораздо больше предупреждающих флагов, не охватываемых -Wall, отсюда и вопрос, и список, который я предоставляю.

Macmade
источник
9
Я испытываю желание сказать, что это, вероятно, следовало бы оставить в переполнении стека, так как это своего рода вопрос об использовании инструментов, но мы увидим, как меняется сообщество.
Адам Лир
Спасибо за комментарий:) В некотором смысле, я согласен с вами, и именно поэтому я первоначально разместил это в StackOverflow (также потому, что я здесь не обычный пользователь, позор мне). Получил много просмотров, голосов и т. Д. ... Но ни одного ответа ... И как люди предложили его перенести ... Ну, посмотрим
:)
Ну, я надеюсь, что мы можем дать вам хороший ответ. Удачи. :)
Адам Лир
Для C и gcc -Wwrite-stringsделает его компилятором языка очень похожим, но не совсем C. По этой причине я не использую эту опцию. Кроме тех, которые вы указываете, я использую -pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2и -Wno-missing-braces -Wno-missing-field-initializersдля вполне разумного инициализатора struct whatever obj = {0};. Также я считаю, что это -Wconversionдает больше «спама», чем полезных предупреждений :)
pmg

Ответы:

158

Для контекста, я разработчик Clang, работающий в Google. В Google мы внедрили диагностику Clang (по существу) всем нашим разработчикам на C ++, и мы также рассматриваем предупреждения Clang как ошибки. Как разработчик Clang и один из крупнейших пользователей диагностики Clang, я постараюсь пролить свет на эти флаги и то, как их можно использовать. Обратите внимание, что все, что я описываю, в целом применимо к Clang, а не специфично для C, C ++ или Objective-C.

TL; DR версия: пожалуйста, используйте -Wallи -Werrorкак минимум для любого нового кода, который вы разрабатываете. Мы (разработчики компилятора) добавляем сюда предупреждения по веским причинам: они находят ошибки. Если вы найдете предупреждение, которое ловит вас за ошибки, включите его. Попробуйте -Wextraздесь кучу хороших кандидатов. Если один из них слишком шумный для вас, чтобы использовать его с выгодой, сообщите об ошибке . Если вы пишете код, который содержит «очевидную» ошибку, но компилятор не предупредил об этом, сообщите об ошибке.

Теперь для длинной версии. Сначала немного информации о группах предупреждений. Существует много группировок предупреждений в Clang (и в ограниченной степени в GCC). Некоторые из них имеют отношение к этой дискуссии:

  • По-умолчанию: эти предупреждения всегда включены, если вы явно не отключите их.
  • -Wall: Это предупреждение о том , что разработчики имеют высокую уверенность в обеих их стоимости и низком уровне ложно-позитивности.
  • -WextraЭто предупреждения, которые считаются ценными и обоснованными (т. Е. Они не содержат ошибок), но могут иметь высокий уровень ложноположительных результатов или общие философские возражения.
  • -WeverythingЭто безумная группа, которая буквально включает каждое предупреждение в Clang. Не используйте это в своем коде. Он предназначен исключительно для разработчиков Clang или для изучения существующих предупреждений .

Есть два основных критерия, упомянутых выше, которые определяют, куда направляются предупреждения в Clang, и давайте выясним, что они на самом деле означают. Первый - это потенциальная ценность конкретного появления предупреждения. Это ожидаемое преимущество для пользователя (разработчика), когда предупреждение срабатывает и правильно определяет проблему с кодом.

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

Для предупреждений Clang это значение должно быть с точки зрения правильности , а не с точки зрения стиля, вкуса или соглашений о кодировании. Это ограничивает набор доступных предупреждений, исключая часто запрашиваемые предупреждения, такие как предупреждение, всякий раз, когда {}s не используются в теле ifоператора. Clang также очень нетерпим к ложным срабатываниям . В отличие от большинства других компиляторов, он будет использовать невероятное разнообразие источников информации для сокращения ложных срабатываний, включая точное написание конструкции, наличие или отсутствие дополнительных '()', приведений или даже макросов препроцессора!

Теперь давайте возьмем несколько реальных примеров предупреждений от Clang и посмотрим, как они классифицируются. Во-первых, предупреждение по умолчанию:

% nl x.cc
     1  class C { const int x; };

% clang -fsyntax-only x.cc
x.cc:1:7: warning: class 'C' does not declare any constructor to initialize its non-modifiable members
class C { const int x; };
      ^
x.cc:1:21: note: const member 'x' will never be initialized
class C { const int x; };
                    ^
1 warning generated.

Здесь не требуется флаг, чтобы получить это предупреждение. Обоснование состоит в том, что этот код никогда не является действительно правильным, давая высокое значение предупреждения , и предупреждение срабатывает только на коде, который Clang может доказать, попадает в этот сегмент, давая ему нулевой уровень ложных срабатываний .

% nl x2.cc
     1  int f(int x_) {
     2    int x = x;
     3    return x;
     4  }

% clang -fsyntax-only -Wall x2.cc
x2.cc:2:11: warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]
  int x = x;
      ~   ^
1 warning generated.

Clang требует -Wallфлаг для этого предупреждения. Причина в том, что существует нетривиальный объем кода, который использовал (хорошо или плохо) шаблон кода, о котором мы предупреждаем, чтобы преднамеренно создать неинициализированное значение. С философской точки зрения, я не вижу в этом смысла, но многие другие не согласны, и реальность этого разногласия заключается в том, что движет предупреждением под -Wallфлагом. Он по-прежнему имеет очень высокое значение и очень низкий уровень ложноположительных результатов , но на некоторых кодовых базах он не является стартовым.

% nl x3.cc
     1  void g(int x);
     2  void f(int arr[], unsigned int size) {
     3    for (int i = 0; i < size; ++i)
     4      g(arr[i]);
     5  }

% clang -fsyntax-only -Wextra x3.cc
x3.cc:3:21: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
  for (int i = 0; i < size; ++i)
                  ~ ^ ~~~~
1 warning generated.

Это предупреждение требует -Wextraфлажка. Причина в том, что существуют очень большие кодовые базы, в которых несоответствующий знак сравнений встречается крайне часто. Хотя это предупреждение обнаруживает некоторые ошибки, вероятность того, что код будет ошибкой, когда пользователь пишет, в среднем достаточно низкая. Результатом является чрезвычайно высокий уровень ложноположительных результатов . Тем не менее, когда в программе есть ошибка из-за странных правил продвижения, часто очень трудно сделать это предупреждение, когда она отмечает ошибку , имеющую относительно высокое значение . Как следствие, Clang предоставляет это и выставляет это под флагом.

Как правило, предупреждения не живут долго вне -Wextraфлага. Clang очень старается не реализовывать предупреждения, которые не видят регулярного использования и тестирования. Дополнительные предупреждения, включенные -Weverythingобычно, являются предупреждениями при активной разработке или с активными ошибками. Либо они будут исправлены и помещены под соответствующие флаги, либо они должны быть удалены.

Теперь, когда у нас есть понимание того, как эти вещи работают с Clang, давайте попробуем вернуться к первоначальному вопросу: какие предупреждения вы должны включить для своего развития? Ответ, к сожалению, зависит от этого. Примите во внимание следующие вопросы, чтобы определить, какие предупреждения лучше всего подходят для вашей ситуации.

  • Есть ли у вас контроль над всем вашим кодом, или часть его является внешним?
  • Каковы твои цели? Ловить ошибки или писать лучший код?
  • Какова ваша ложноположительная терпимость? Готовы ли вы написать дополнительный код для регулярного отключения предупреждений?

Прежде всего, если вы не контролируете код, не пытайтесь включить туда дополнительные предупреждения. Будьте готовы выключить некоторые. В мире много плохого кода, и вы не сможете исправить все это. Все в порядке. Старайтесь найти способ сосредоточить свои усилия на коде, которым вы управляете.

Затем выясните, что вы хотите из своих предупреждений. Это разные для разных людей. Clang попытается предупредить без каких-либо опций о вопиющих ошибках или шаблонах кода, для которых у нас есть давний исторический прецедент, указывающий, что уровень ошибок чрезвычайно высок. Благодаря этому -Wallвы получите гораздо более агрессивный набор предупреждений, направленных на выявление наиболее распространенных ошибок, которые разработчики Clang наблюдали в коде C ++. Но с обоими из них уровень ложноположительных результатов должен оставаться довольно низким.

Наконец, если вы абсолютно готовы замолчать * ложноположительные * на каждом шагу, сделайте это -Wextra. Сообщайте об ошибках, если вы замечаете предупреждения, которые содержат много реальных ошибок, но имеют ложные или бессмысленные ложные срабатывания. Мы постоянно работаем , чтобы найти способы , чтобы принести больше и больше ошибок ознакомительной логики представить в -Wextraв -Wallгде мы можем избежать ложных срабатываний.

Многие найдут, что ни один из этих вариантов не подходит именно им. В Google мы отключили некоторые предупреждения -Wallиз-за большого количества существующего кода, который нарушил предупреждение. Мы также включили некоторые предупреждения явно, хотя они не включены -Wall, потому что они имеют для нас особенно высокую ценность. Ваш пробег будет варьироваться, но, вероятно, будет меняться аналогичным образом. Часто бывает гораздо лучше включить несколько ключевых предупреждений, а не все -Wextra.

Я бы посоветовал всем включить -Wallлюбой не устаревший код. Для нового кода предупреждения здесь почти всегда полезны и действительно улучшают опыт разработки кода. И наоборот, я бы посоветовал всем не включать флаги за пределами -Wextra. Если Вы нашли предупреждение Clang , что -Wextra не включает в себя , но который оказывается совсем ценным для вас, просто файл ошибки , и мы , вероятно , можем поставить его под -Wextra. Если вы явно включите какое-то подмножество предупреждений, -Wextraбудет сильно зависеть от вашего кода, вашего стиля кодирования и от того, проще ли поддерживать этот список, чем исправить все, что было обнаружено -Wextra.

Из списка предупреждений OP (который включает оба -Wallи -Wextra) только следующие предупреждения не охватываются этими двумя группами (или включены по умолчанию). Первая группа подчеркивает, почему чрезмерная зависимость от флагов явного предупреждения может быть плохой: ни один из них даже не реализован в Clang! Они принимаются в командной строке только для совместимости с GCC.

  • -Wbad-function-cast
  • -Wdeclaration-after-statement
  • -Wmissing-format-attribute
  • -Wmissing-noreturn
  • -Wnested-externs
  • -Wnewline-eof
  • -Wold-style-definition
  • -Wredundant-decls
  • -Wsequence-point
  • -Wstrict-prototypes
  • -Wswitch-default

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

  • -Wformat-nonliteral - Подмножество -Wformat=2
  • -Wshorten-64-to-32 - Подмножество -Wconversion
  • -Wsign-conversion - Подмножество -Wconversion

Есть также выбор предупреждений, которые более категоричны. Они имеют дело с вариантами языкового диалекта, а не с ошибочным или не ошибочным кодом. За исключением того -Wwrite-strings, что все это предупреждения для языковых расширений, предоставляемых Clang. Предупреждает ли Clang об их использовании, зависит от распространенности расширения. Clang стремится к совместимости с GCC, и поэтому во многих случаях облегчает это с помощью неявных языковых расширений, которые широко используются. -Wwrite-stringsКак прокомментировал OP, это флаг совместимости от GCC, который фактически изменяет семантику программы. Я глубоко сожалею об этом флаге, но мы должны поддержать его из-за наследия, которое оно имеет сейчас.

  • -Wfour-char-constants
  • -Wpointer-arith
  • -Wwrite-strings

Остальные опции, которые фактически включают потенциально интересные предупреждения, таковы:

  • -Wcast-align
  • -Wconversion
  • -Wfloat-equal
  • -Wformat=2
  • -Wimplicit-atomic-properties
  • -Wmissing-declarations
  • -Wmissing-prototypes
  • -Woverlength-strings
  • -Wshadow
  • -Wstrict-selector-match
  • -Wundeclared-selector
  • -Wunreachable-code

Причина, по которой они отсутствуют -Wallили -Wextraне всегда понятна. Для многих из них, на самом деле они основаны на предупреждения GCC ( -Wconversion, -Wshadowи т.д.) и как таковой Clang пытается имитировать поведение GCC в. Мы постепенно разбиваем некоторые из них на более мелкие и полезные предупреждения. Те, у кого больше шансов попасть в одну из групп предупреждений верхнего уровня. Тем не менее, чтобы выбрать одно предупреждение, -Wconversionоно настолько широко, что в обозримом будущем оно, скорее всего, останется своей собственной категорией «высшего уровня». Некоторые другие предупреждения, которые имеет GCC, но которые имеют низкую ценность и высокий уровень ложноположительных результатов, могут быть отнесены к аналогичной ничейной земле.

Другие причины, по которым их нет в одной из больших групп, включают в себя простые ошибки, очень существенные ложноположительные проблемы и предупреждения в процессе разработки. Я собираюсь изучить ошибки, которые я могу определить. Все они в конечном итоге должны перейти в соответствующий большой флаг или быть удалены из Clang.

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

Чендлер Кэррут
источник
8
Отличный ответ! Большое спасибо. Мне нужно будет перечитать это несколько раз, но я скоро вернусь с некоторыми комментариями. Еще раз спасибо!
Macmade
1
@Chandler Carruth: Отличный ответ! Но не могли бы вы обновить его, если что-то изменилось? Я использую все ваши предупреждения в вашем последнем списке, но, возможно, некоторые уже перенесены в Wextra?
gartenriese
Это отличный пост. Но обнаружение новых предупреждений, как я обнаружил, является чрезвычайно хорошим использованием флага, поэтому я разочарован тем, что вы, кажется, настолько негативно настроены по отношению к группе «все», несмотря на признание того, что это полезно для такого рода исследований.
Кайл Стрэнд,
@Chandler Carruth: Я согласен с обоснованием предупреждения о «никогда не правильном» коде. Просто из-за того, что нет -Wnoнеобходимости выключать, warn_no_constructor_for_refconst PITA использует Boost.ConceptCheck и тому подобное: github.com/boostorg/concept_check/blob/…
mloskot
2

Я не использую Clang, но я надеюсь, что вы тоже не против того, чтобы узнать мое мнение. Я также использую максимальный уровень предупреждений (полагаю, что это означает -Wall) и трактую предупреждения как ошибки (я предполагаю, что это означает -Werror), и я отключаю только те несколько предупреждений, которые не имеют особого смысла. На самом деле, меня раздражает тот факт, что некоторые очень полезные предупреждения не выдаются компилятором C #, и для их просмотра необходимо запустить специальные инструменты анализа кода. Мне нравятся предупреждения, потому что они помогают мне обнаруживать небольшие ошибки и ошибки в моем коде. Они мне так нравятся, что когда выдается предупреждение для фрагмента кода, который, как я знаю, является правильным, я все равно реорганизую этот фрагмент кода, так что предупреждение не будет выдано, вместо того, чтобы отключить предупреждение, или --even хуже - позволить этому появиться и игнорировать это. Я думаю, что предупреждения потрясающие,

Майк Накис
источник
1
Спасибо за ответ. К сожалению, -Wallпредоставляет только основные предупреждения. Многие существующие предупреждающие флаги не охвачены -Wall, поэтому список в моем вопросе.
Macmade
0

Обычно я использую настройки Xcode по умолчанию для нового проекта; это обеспечивает хороший баланс между полезным и раздражающим. Любой конкретный список предупреждений и другие параметры компилятора будут основываться в значительной степени либо на личных предпочтениях, либо на требованиях проекта, поэтому я не думаю, что есть большая польза от попыток просмотреть ваш список и привести аргументы за или против конкретных предупреждений. Если это работает для вас, отлично. Если вы обнаружили, что допустили ошибку, которую компилятор должен был бы обнаружить, и вам нужна помощь в этом в будущем, найдите опцию компилятора, чтобы предупредить об этом и включить ее.

Одна вещь, которую многие программисты рекомендуют, - это включить опцию «Рассматривать предупреждения как ошибки» (-Werror), чтобы предотвратить успешное завершение сборок при наличии каких-либо предупреждений.

Калеб
источник
Спасибо за ответ! Я согласен с -Werror. На самом деле, так как я использую Clang, я установил все эти предупреждения с помощью прагмы (#pragma лязг диагностики), и они установлены к фатальным ошибкам, так это то же самое , как -Werror:)
Macmade
0

Я думаю, что лучшим доказательством того, что люди заботятся о предупреждениях, является тот факт, что они существуют и широко используются. Я твердо убежден, что чем больше ошибок будет обнаружено во время компиляции, тем лучше. Если бы это не было широко распространенным убеждением, все использовали бы слабые динамически типизированные языки, потому что их компиляторы или интерпретаторы дают вам гораздо больше свободы действий. Наоборот, даже на языках сценариев использование strictфлагов популярно. В статических строго типизированных языках люди не только -Wall -Werrorчасто используют , но они часто находят предупреждения настолько ценными, что им нужно еще больше , поэтому они платят за инструменты статического анализа, такие как Coverity , единственной целью которых является предоставление предупреждений.

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

node = node; // Shut up compiler warning
Карл Билефельдт
источник
Спасибо за Ваш ответ. Проблема в том, что я часто вижу людей, которые работают без них -Werrorи фактически игнорируют предупреждения, выдаваемые компилятором. Или, если они делают, они просто придерживаются -Wall. Большинство флагов, которые я предоставляю в своем вопросе, не охвачены -Wall, и я лично думаю, что они тоже важны. Так я просто параноик? ; )
Macmade
0

Вообще, я больше склоняюсь к -Wall и определенно верю, в том числе и к -Werror. Модуль никогда не должен был ожидать предупреждений. В конечном итоге вы получите еще одно предупреждение и пропустите его. И это будет на самом деле проблема.

Это также зависит от того, добавляете ли вы «-Wall -Werror» в новый или старый проект. Если он новый, то сделайте это и потребуйте совершенства, иначе вы, вероятно, будете в течение нескольких дней или недель редактировать и тестировать. Я только что сделал это на более старом, но небольшом проекте, и потратил два или три дня на удаление предупреждений. Я думаю, что код теперь чище.

Другими словами, попробуйте и посмотрите.

похотливый

Рэнди Стегбауэр
источник
1
Если вас беспокоит только -Wallи -Werror, я думаю, вам следует перечитать вопрос. Многие флаги предупреждения не покрыты -Wall. В любом случае, спасибо за ответ.
Macmade