Кланг-попереджувальні прапори для розробки Objective-C


86

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

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

На мій погляд, це також гарний інструмент для навчання щодня, навіть якщо ви досвідчений програміст.

Для суб'єктивної частини цього питання мені цікаво почути інших розробників (головним чином C, Objective-C і C ++) з цієї теми.
Чи насправді вам цікаві такі речі, як педантичні попередження тощо? І якщо так чи ні, то чому?

Тепер про Objective-C я нещодавно повністю перейшов на ланцюжок інструментів LLVM (з Clang), а не на GCC.

У своєму виробничому коді я зазвичай встановлюю ці попереджувальні прапори (явно, навіть якщо деякі з них можуть бути прикриті -Wall):

  • -Стінка
  • -Wbad-функція-ролях
  • -Wcast-вирівняти
  • -Перетворення
  • -Декларація після заяви
  • -Зазначені-реалізації
  • -Векстра
  • -Полово-рівне
  • -Wformat = 2
  • -Wformat-nonliteral
  • -Чати-чар-константи
  • -Явні-атомні властивості
  • -Весінні брекети
  • -Зміщення-декларації
  • -Іміціалізатори поля
  • -Wmissing-format-attribute
  • -Вімсинг-норетурн
  • -Вітмінг-прототипи
  • -Незмінені-зовнішні
  • -Wnewline-eof
  • -Wold-style-визначення
  • -Висота довжини-струни
  • -Загляди
  • -Замінник-ариф
  • -Перевісні декласи
  • -Резервний тип
  • -Послідок-бал
  • -Тень
  • -Скоротити-64-до -32
  • -Wsign-порівняти
  • -Wsign-перетворення
  • -Сторочні прототипи
  • -Стримка-селектор-відповідність
  • -Вимикач
  • -Wswitch-за замовчуванням
  • -Wswitch-enum
  • -Виявлений селектор
  • -Вінніціалізований
  • -Невідомі-прагми
  • -Недоступний код
  • -Використана функція
  • -Використаний ярлик
  • -Використаний параметр
  • -Використане значення
  • -Використана змінна
  • -Написати рядки

Мені цікаво почути, що про це мають сказати інші розробники.

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

EDIT

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


9
Мені спокуса сказати, що це, мабуть, повинно було залишитися на стеку Overflow, оскільки це начебто питання щодо використання інструменту, але ми побачимо, як громада змінюється.
Адам Лір

Дякую за коментар:) Я певним чином погоджуюся з вами, і саме тому я спочатку опублікував це на 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тут купу хороших кандидатів. Якщо один із них занадто галасливий, щоб ви з користю користувалися, подайте помилку . Якщо ви пишете код, який містить "очевидну" помилку, але компілятор не попереджав про це, подайте помилку.

Тепер про довгу версію. Спочатку деякі відомості про групування попереджувальних прапорів. У Кланг (і в обмеженій мірі в GCC) існує багато "групувань" попереджень. Кілька релевантних для цієї дискусії:

  • За замовчуванням: Ці попередження завжди активовані, якщо ви явно не відключите їх.
  • -Wall: Це попередження про те, що розробники мають високу довіру як до їх вартості, так і до низької помилково-позитивної.
  • -Wextra: Це попередження, які, як вважають, є цінними та здоровими (тобто вони не є помилками), але вони можуть мати високі помилково-позитивні оцінки або загальні філософські заперечення.
  • -Weverything: Це божевільна група, яка буквально дає можливість будь-яке попередження в Кланг. Не використовуйте це у своєму коді. Він призначений строго для розробників Clang або для вивчення того, які існують попередження .

Вище згадані два основні критерії, які направляють, куди надходять попередження в Кланг, і давайте уточнимо, що це насправді означає. Перший - потенційне значення конкретного явища попередження. Це очікувана вигода для користувача (розробника), коли попередження спрацьовує та правильно ідентифікує проблему з кодом.

Другий критерій - це ідея хибнопозитивних звітів. Це ситуації, коли попередження спрацьовує на коді, але потенційна проблема, що цитується, насправді не виникає через контекст чи інше обмеження програми. Код, про який попереджено, насправді поводиться правильно. Вони особливо погані, коли попередження ніколи не було спрямоване на цей код. Натомість саме дефіцит у виконанні попередження викликає його загоряння.

Для попереджень Кланг значення повинно бути з точки зору правильності , а не з точки зору стилю, смаку чи умовності кодування. Це обмежує набір наявних попереджень, виключаючи часто запитувані попередження, такі як попередження, коли {}s не використовується навколо тіла ifзаяви. Кланг також дуже нетерпимий до хибнопозитивних . На відміну від більшості інших компіляторів, він використовуватиме неймовірну різноманітність джерел інформації для обрізки помилкових позитивних результатів, включаючи точне написання конструкції, наявність або відсутність зайвих '()', кастів або навіть препроцесорних макросів!

Тепер давайте візьмемо кілька реальних прикладів попереджень від 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.

Тут не потрібен прапор, щоб отримати це попередження. Обґрунтування полягає в тому, що це код ніколи не є правильним, надаючи попередження високе значення , і попередження спрацьовує лише за кодом, який може довести Клэнг, потрапляє в це відро, даючи йому нульову помилкову швидкість.

% 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прапор. Причина полягає в тому, що існує дуже велика база кодів, де невідповідний знак для порівнянь надзвичайно поширений. Хоча це попередження виявляє деякі помилки, ймовірність того, що код є помилкою, коли користувач пише, що він в середньому досить низький. Результат - надзвичайно висока помилково-позитивна норма. Однак, коли в програмі є помилка через дивні правила просування, часто це надзвичайно тонко робить таке попередження, коли прапорці помилки мають відносно високе значення . Як наслідок, Кланг надає це та виставляє його під прапором.

Зазвичай попередження не існують довго поза -Wextraпрапором. Кланг дуже намагається не застосовувати попередження, які не бачать регулярного використання та тестування. Додаткові попередження, -Weverythingвключені зазвичай, є попередженнями під час активного розвитку або з активними помилками. Або вони будуть закріплені та розміщені під відповідними прапорами, або їх слід зняти.

Тепер, коли ми зрозуміли, як ці речі працюють з Кланг, давайте спробуємо повернутися до початкового питання: які застереження слід увімкнути для розвитку? Відповідь, на жаль, залежить від цього. Розгляньте наступні питання, які допоможуть визначити, які застереження найкраще підходять для вашої ситуації.

  • Чи маєте ви контроль над усім своїм кодом, чи частина його зовнішня?
  • Які ваші цілі? Ловити помилки чи писати кращий код?
  • Яка ваша хибнопозитивна толерантність? Чи готові ви регулярно писати додатковий код, щоб замовчувати попередження?

По-перше, якщо ви не контролюєте код, не намагайтеся ввімкнути додаткові попередження. Будьте готові відключити. У світі багато поганого коду, і ви, можливо, не зможете все це виправити. Що це нормально. Працюйте, щоб знайти спосіб зосередити свої зусилля на контрольованому вами коді .

Далі з’ясуйте, що ви хочете від своїх попереджень. Це різне для різних людей. Clang намагатиметься попередити без будь-яких варіантів нападкових помилок або моделей коду, для яких у нас є давній історичний прецедент, який свідчить про надзвичайно високий рівень помилок. Вмикаючи, -Wallви збираєтеся отримати набагато більш агресивний набір попереджень, спрямованих на пошук найпоширеніших помилок, які спостерігали розробники Clang у коді C ++. Але в обох випадках помилково-позитивний показник повинен залишатися досить низьким.

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

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

Я б закликав усіх увімкнути -Wallбудь-який не спадковий код. Щодо нового коду, попередження тут майже завжди цінні та дійсно покращують досвід розробки коду. І навпаки, я б закликав усіх не допускати прапорів за межами -Wextra. Якщо ви знайшли попередження Clang , який -Wextra не включає в себе , але який виявляється зовсім цінним для вас, просто файл помилки , і ми , ймовірно , можемо поставити його під -Wextra. Від того, чи явно ви ввімкнете деякий підмножина попереджень у, -Wextraбуде сильно залежати від вашого коду, вашого стилю кодування та того, чи підтримувати цей список простіше, ніж виправити все, що не виявлено -Wextra.

З переліку попереджень ОП (які включають обидва -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. Чи попередить Кланг про їх використання, залежить від поширеності розширення. Кланг прагне до сумісності з GCC, і тому у багатьох випадках це полегшує ситуацію з неявними розширеннями мови, які широко використовуються. -Wwrite-strings, як коментує ОП, це прапор сумісності від 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 і надає деяку інформацію для тих, хто намагається вибрати набір попереджень для їх використання або використання їхньої компанії.


8
Видатна відповідь! Дуже дякую. Мені потрібно перечитати кілька разів, але незабаром я повернусь із деякими коментарями. Знову дякую!
Макмаде

1
@Chandler Carruth: Чудова відповідь! Але чи можете ви оновити його, якщо щось змінилося? Я використовую всі ваші попередження у вашому останньому списку, але, можливо, деякі вже переміщені до Wextra?
gartenriese

Це відмінна посада. Але пошук нових попереджень - це, я вважаю, надзвичайно вдалий варіант використання прапора, тому я розчарований, що ви настільки негативно ставитесь до групи "все", незважаючи на те, що визнаєте, що це корисно для такого роду розвідки.
Кайл Странд

@Chandler Carruth: Я погоджуюсь з обґрунтуванням попередження про "ніколи не дійсно правильний" код. Просто відсутність -Wnoвідключення warn_no_constructor_for_refconst змушує PITA використовувати Boost.ConceptCheck тощо: github.com/boostorg/concept_check/blob/…
mloskot

2

Я не використовую Кланг, але я сподіваюся, що ви не будете проти того, щоб мати мою думку. Я також переходжу з максимальним рівнем попередження (я вважаю, що це означає -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, і я особисто вважаю, що вони також є важливими. Так я просто параноїк? ; )
Макмаде

0

Як правило, я більше схиляюсь до -Wall, і, безумовно, вірю в також -Werror. Модуль ніколи не повинен був очікувати попереджень. Ви зрештою отримаєте ще одне попередження і пропустите його. І це буде саме те, що насправді є проблемою.

Це також залежить від того, додаєте ви "-Wall -Werror" до нового чи старого проекту. Якщо він новий, тоді задумайтесь про це та вимагайте досконалості, інакше ви, ймовірно, протягом днів чи тижнів редагуєте та тестуєте. Я щойно робив це на старшому дещо невеликому проекті, і я витратив два-три дні, знімаючи попередження. Я думаю, що зараз код чистіший.

Іншими словами, спробуйте і подивіться.

Ренді


1
Якщо ви тільки стурбовані -Wallі -Werror, я думаю , ви повинні перечитати питання. Багато попереджувальних прапорів не охоплені -Wall. Дякую за відповідь, все одно.
Macmade
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.