Drapeaux d'avertissement Clang pour le développement Objective-C

86

En tant que programmeur C & Objective-C, je suis un peu paranoïaque avec les drapeaux d'avertissement du compilateur.
J'essaie généralement de trouver une liste complète de drapeaux d'avertissement pour le compilateur que j'utilise et d'activer la plupart d'entre eux, sauf si j'ai une très bonne raison de ne pas l'activer.

Personnellement, je pense que cela peut réellement améliorer les compétences de codage, ainsi que la portabilité potentielle du code, éviter certains problèmes, car cela vous oblige à prendre conscience de chaque détail, de problèmes potentiels de mise en œuvre et d'architecture, etc.

C'est aussi à mon avis un bon outil d'apprentissage au quotidien, même si vous êtes un programmeur expérimenté.

Pour la partie subjective de cette question, je voudrais entendre d’autres développeurs (principalement C, Objective-C et C ++) sur ce sujet.
Vous souciez-vous réellement de choses comme des avertissements pédantiques, etc.? Et si oui ou non, pourquoi?

Maintenant, à propos d’Objective-C, je suis récemment passé complètement à la chaîne d’outils LLVM (avec Clang), au lieu de GCC.

Sur mon code de production, je règle habituellement ces drapeaux d'avertissement (explicitement, même si certains d'entre eux peuvent être couverts par -Wall):

  • -Mur
  • -Wbad-function-cast
  • -Wcast-align
  • -Conversion
  • -Wdeclaration-after-statement
  • -Wdeprecated-implémentations
  • -Wextra
  • -Wfloat-égal
  • -Wformat = 2
  • -Wformat-nonliteral
  • -Quatre-constantes-char
  • -Wimplicit-atomic-properties
  • -Accessoires-manquants
  • -Missions déclarées
  • -Initialiseurs de champs manquants
  • Attribut de format -Wmissing
  • -Voulant-noreturn
  • Prototypes manquants
  • -Wnested-externs
  • -Wnewline-eof
  • -Wold-style-definition
  • -Woverlength-strings
  • -Parenthèses
  • -Wpointer-arith
  • -Wredundant-decls
  • Type de retour
  • -Point de séquence
  • -Ours
  • -Wshorten-64-à-32
  • -Signature-comparer
  • -Signature-conversion
  • -Protection-prototypes
  • -Wstrict-selector-match
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • Sélecteur de lunette
  • -Wuninitialisé
  • Pragmas méconnus
  • -Le code inaccessible
  • -Fonction non utilisée
  • -Etiquette non utilisée
  • Paramètre non utilisé
  • -Wunused-value
  • -Variable non utilisée
  • -Write-strings

Je suis intéressé à entendre ce que les autres développeurs ont à dire à ce sujet.

Par exemple, pensez-vous que j'ai raté un drapeau particulier pour Clang (Objective-C) et pourquoi?
Ou pensez-vous qu'un drapeau particulier n'est pas utile (ou pas du tout voulu), et pourquoi?

MODIFIER

Pour clarifier la question, notez que -Wallne fournit que quelques avertissements de base.
Ce sont en réalité beaucoup plus de drapeaux d'avertissement, non couverts par -Wall, d'où la question et la liste que je fournis.

Macmade
la source
9
Je suis tenté de dire que cela aurait probablement dû rester sur Stack Overflow, puisqu'il s'agit en quelque sorte d'une question d'utilisation des outils, mais nous verrons comment la communauté évolue.
Adam Lear
Merci pour le commentaire:) En un sens, je suis d’accord avec vous, et c’est pourquoi j’ai initialement posté ceci sur StackOverflow (également parce que je ne suis pas un utilisateur régulier ici, honte à moi). Vous avez eu beaucoup de points de vue, de votes positifs, etc ... mais pas une seule réponse ... Et comme les gens ont suggéré de le déplacer ... Bon, voyons ce qui
suit:)
J'espère que nous pourrons vous donner une bonne réponse. Bonne chance. :)
Adam Lear
Pour C et gcc, -Wwrite-stringscela en fait un compilateur d’un langage très similaire mais pas tout à fait C. Pour cette seule raison, je n’utilise pas cette option. Autres que ceux que vous spécifiez, j'utilise -pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2et -Wno-missing-braces -Wno-missing-field-initializerspour l'initialiseur parfaitement raisonnable struct whatever obj = {0};. Aussi, je trouve que cela -Wconversiondonne plus de "spam" que d’avertissements utiles :)
pmg

Réponses:

158

Pour le contexte, je suis un développeur Clang travaillant chez Google. Chez Google, nous avons étendu les diagnostics de Clang à (essentiellement) tous nos développeurs C ++, et nous traitons également les avertissements de Clang comme des erreurs. En tant que développeur Clang et l'un des principaux utilisateurs des diagnostics de Clang, je vais essayer de faire la lumière sur ces indicateurs et sur la manière dont ils peuvent être utilisés. Notez que tout ce que je décris est génériquement applicable à Clang, et non spécifique à C, C ++ ou Objective-C.

TL; DR Version: Veuillez utiliser -Wallet -Werrorau minimum tout nouveau code que vous développez. Nous (les développeurs du compilateur) ajoutons ici des avertissements pour de bonnes raisons: ils trouvent des bogues. Si vous trouvez un avertissement qui attrape des bugs pour vous, activez-le également. Essayez d' -Wextraavoir un groupe de bons candidats ici. Si l’un d’eux est trop bruyant pour que vous l’utilisiez de manière rentable, signalez un bogue . Si vous écrivez du code contenant un bogue "évident" mais que le compilateur ne vous en a pas averti, créez un bogue.

Maintenant pour la version longue. Tout d'abord quelques informations sur les groupes d'indicateurs d'avertissement. Il y a beaucoup de "groupements" d'avertissements dans Clang (et dans une moindre mesure dans GCC). Certains qui sont pertinents pour cette discussion:

  • Activé par défaut: ces avertissements sont toujours activés, sauf si vous les désactivez explicitement.
  • -Wall: Ce sont des avertissements que les développeurs ont une grande confiance dans leur valeur et un faible taux de faux positifs.
  • -Wextra: Ce sont des avertissements qui sont considérés comme précieux et valables (c’est-à-dire qu’ils ne sont pas buggés), mais ils peuvent présenter des taux de faux positifs élevés ou des objections philosophiques communes.
  • -Weverything: Ceci est un groupe insensé qui active littéralement tous les avertissements dans Clang. Ne l'utilisez pas dans votre code. Il est destiné uniquement aux développeurs Clang ou à l'exploration des avertissements existants .

Il existe deux critères principaux mentionnés ci-dessus qui guident les avertissements dans Clang, et clarifions leur signification. Le premier est la valeur potentielle d'une occurrence particulière de l'avertissement. C'est l'avantage attendu par l'utilisateur (développeur) lorsque l'avertissement se déclenche et qui identifie correctement un problème avec le code.

Le deuxième critère est l’idée de déclarations faussement positives . Ce sont des situations où l'avertissement se déclenche sur le code, mais le problème potentiel évoqué ne se produit en fait pas à cause du contexte ou d'une autre contrainte du programme. Le code sur lequel on vous a prévenu se comporte correctement. Celles-ci sont particulièrement dommageables lorsque l'avertissement n'a jamais été conçu pour déclencher ce code. Au lieu de cela, c'est une déficience dans la mise en œuvre de l'alerte qui la déclenche là.

Pour les avertissements Clang, la valeur doit être exprimée en termes d' exactitude , et non en termes de style, de goût ou de conventions de codage. Cela limite l'ensemble des avertissements disponibles, en excluant les avertissements souvent demandés tels que les avertissements lorsque {}aucun s n'est utilisé autour du corps d'une ifinstruction. Clang est également très intolérant envers les faux positifs . Contrairement à la plupart des autres compilateurs, il utilisera une incroyable variété de sources d’information pour élaguer les faux positifs, notamment l’orthographe exacte de la construction, la présence ou non de macros «()» supplémentaires, ou même de préprocesseurs!

Prenons maintenant des exemples concrets d’avertissements de Clang et examinons leur classement. Tout d'abord, un avertissement par défaut:

% 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.

Ici, aucun drapeau n'était requis pour obtenir cet avertissement. La raison en est que ce code n’est jamais vraiment correct, ce qui donne une valeur élevée à l’avertissement , et cet avertissement ne déclenche que le code que Clang peut prouver tombe dans ce compartiment, ce qui lui donne un taux de zéro faux positif .

% 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 requiert le -Walldrapeau pour cet avertissement. La raison en est qu’il existe une quantité non négligeable de code qui a utilisé (pour le meilleur ou pour le pire) le modèle de code sur lequel nous avertissons de produire intentionnellement une valeur non initialisée. Philosophiquement, je ne vois pas l'intérêt de cela, mais beaucoup d'autres sont en désaccord et la réalité de cette divergence d'opinion est ce qui motive l'avertissement sous le -Walldrapeau. Il a toujours une valeur très élevée et un taux de faux positifs très bas , mais sur certains codes, il est non partant.

% 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.

Cet avertissement nécessite le -Wextradrapeau. La raison en est qu’il existe de très grandes bases de code où les comparaisons de signatures mal appariées sont extrêmement courantes. Bien que cet avertissement trouve des bogues, la probabilité que le code soit un bogue lorsque l'utilisateur l'écrit est en moyenne assez faible. Le résultat est un taux extrêmement élevé de faux positifs . Cependant, lorsqu'un programme contient un bogue en raison d'étranges règles de promotion, il est souvent extrêmement subtil de rendre cet avertissement lorsqu'il signale qu'un bogue a une valeur relativement élevée . En conséquence, Clang le fournit et l’expose sous un drapeau.

Généralement, les avertissements ne survivent pas longtemps en dehors du -Wextradrapeau. Clang essaie très fort de ne pas mettre en œuvre des avertissements qui ne sont pas régulièrement utilisés et testés. Les avertissements supplémentaires activés par -Weverythingsont généralement des avertissements en développement actif ou avec des bogues actifs. Soit ils seront fixés et placés sous des drapeaux appropriés, soit ils devraient être supprimés.

Maintenant que nous comprenons comment ces choses fonctionnent avec Clang, essayons de revenir à la question initiale: quels avertissements devriez-vous activer pour votre développement? La réponse est, malheureusement, que cela dépend. Réfléchissez aux questions suivantes pour déterminer les avertissements les mieux adaptés à votre situation.

  • Avez-vous le contrôle de tout votre code, ou est-ce que certains d'entre eux sont externes?
  • Quels sont vos objectifs? Attraper des bugs ou écrire un meilleur code?
  • Quelle est votre tolérance faussement positive? Êtes-vous disposé à écrire du code supplémentaire pour faire taire les avertissements régulièrement?

Tout d'abord, si vous ne contrôlez pas le code, n'essayez pas d'activer des avertissements supplémentaires. Préparez-vous à en éteindre. Il y a beaucoup de mauvais codes dans le monde, et vous ne pourrez peut-être pas tout réparer. C'est bon. Travaillez pour trouver un moyen de concentrer vos efforts sur le code que vous contrôlez.

Ensuite, déterminez ce que vous voulez sortir de vos avertissements. Ceci est différent pour différentes personnes. Clang essaiera d’avertir sans aucune option pour les bogues évidents ou les modèles de code pour lesquels nous avons un précédent historique qui indique que le taux de bogues est extrêmement élevé. En activant cette fonctionnalité, -Wallvous obtiendrez un ensemble d'avertissements beaucoup plus agressif visant à détecter les erreurs les plus courantes observées par les développeurs Clang dans le code C ++. Mais avec ces deux facteurs, le taux de faux positifs devrait rester assez faible.

Enfin, si vous êtes parfaitement disposé à faire taire les * faux-positifs * à chaque tour, foncez -Wextra. Si vous remarquez des avertissements qui attirent de nombreux bogues réels, mais qui ont des faux positifs stupides ou inutiles, vous pouvez créer des bogues. Nous travaillons constamment pour trouver des moyens d'introduire de plus en plus de la logique de recherche de bogues -Wextradans -Walllaquelle nous pouvons éviter les faux positifs.

Beaucoup trouveront qu'aucune de ces options n'est juste pour eux. Chez Google, certains avertissements ont été -Walldésactivés, car de nombreux codes existants ne respectaient pas cet avertissement. Nous avons également activé explicitement certains avertissements, même s'ils ne sont pas activés par -Wall, car ils ont une valeur particulièrement élevée pour nous. Votre kilométrage variera, mais variera probablement de la même manière. Il peut souvent être préférable d’activer quelques avertissements clés plutôt que tous -Wextra.

J'encourage tout le monde à activer le -Wallcode non hérité. Pour les nouveaux codes, les avertissements ici sont presque toujours précieux et améliorent réellement l'expérience de développement de code. Inversement, j'encourage tout le monde à ne pas autoriser les drapeaux au-delà -Wextra. Si vous trouvez un avertissement de Clang qui -Wextra ne comprend pas , mais qui prouve tout précieux pour vous, déposer simplement un bug et nous pouvons probablement le mettre sous -Wextra. Que vous activiez explicitement un sous-ensemble des avertissements dans -Wextradépendra beaucoup de votre code, de votre style de codage et de la pertinence de la gestion de cette liste plutôt que de tout réparer -Wextra.

Parmi la liste d'avertissements de l'OP (qui incluait les deux -Wallet -Wextra), seuls les avertissements suivants ne sont pas couverts par ces deux groupes (ou activés par défaut). Le premier groupe explique pourquoi le recours excessif à des indicateurs d'avertissement explicites peut être mauvais: aucun de ceux-ci n'est même implémenté dans Clang! Ils sont acceptés sur la ligne de commande uniquement pour la compatibilité 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

Les prochains avertissements inutiles dans la liste d'origine sont ceux qui sont redondants avec ceux de cette liste:

  • -Wformat-nonliteral -- Sous-ensemble de -Wformat=2
  • -Wshorten-64-to-32 -- Sous-ensemble de -Wconversion
  • -Wsign-conversion -- Sous-ensemble de -Wconversion

Il existe également une sélection d'avertissements plus catégoriquement différents. Celles-ci traitent de variantes linguistiques du dialecte plutôt que de codes bogués ou non bogués. À l'exception de -Wwrite-strings, tous ces avertissements concernent les extensions de langage fournies par Clang. Que Clang avertisse de leur utilisation dépend de la prévalence de l'extension. Clang vise la compatibilité GCC et, dans de nombreux cas, simplifie ainsi la tâche avec des extensions de langage implicites largement utilisées. -Wwrite-strings, comme indiqué dans l'OP, est un indicateur de compatibilité de GCC qui modifie réellement la sémantique du programme. Je regrette profondément ce drapeau, mais nous devons le soutenir en raison de l'héritage qu'il a aujourd'hui.

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

Les options restantes permettant des avertissements potentiellement intéressants sont les suivantes:

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

La raison pour laquelle ils ne sont pas -Wallou -Wextrane sont pas toujours clairs. Pour beaucoup d' entre eux, ils sont en fait basés sur les avertissements du CCG ( -Wconversion, -Wshadow, etc.) et en tant que telle Clang tente d'imiter le comportement de GCC. Nous en décomposons progressivement certaines en des mises en garde plus fines et utiles. Ceux-ci ont alors une probabilité plus élevée de faire partie de l’un des groupes d’alerte les plus avancés. Cela dit, choisir un seul avertissement -Wconversionest si vaste qu'il restera probablement sa propre catégorie de "premier niveau" dans un avenir prévisible. Certains autres avertissements dont dispose GCC mais qui ont une faible valeur et des taux de faux positifs élevés peuvent être relégués au rang de no man's land.

Parmi les autres raisons pour lesquelles ils ne font pas partie des plus grands compartiments, citons les bogues simples, les très importants problèmes de faux positifs et les avertissements en cours de développement. Je vais examiner le classement des bogues pour ceux que je peux identifier. Ils devraient tous éventuellement migrer vers un grand drapeau de seau approprié ou être retirés de Clang.

J'espère que cela clarifie la situation d'avertissement avec Clang et fournit quelques informations à ceux qui essaient de choisir un ensemble d'avertissements pour leur utilisation ou l'utilisation de leur entreprise.

Chandler Carruth
la source
8
Réponse exceptionnelle! Merci beaucoup. Je devrai le relire à quelques reprises, mais je reviendrai bientôt avec quelques commentaires. Merci encore!
Macmade
1
@ Carrler Carruth: Excellente réponse! Mais pourriez-vous le mettre à jour, si quelque chose a changé? J'utilise tous vos avertissements sur votre dernière liste, mais peut-être que certains sont déjà déplacés vers Wextra?
Gartenriese
C'est un excellent post. Mais trouver de nouveaux avertissements est, j’ai trouvé, un très bon usage du drapeau. Je suis donc déçu de votre attitude si négative vis-à-vis du groupe "tout", même si vous reconnaissez que cela est utile pour ce type d’exploration.
Kyle Strand
@ Chandler Carruth: Je suis d'accord avec la raison d'être de l'avertissement concernant le code "jamais vraiment correct". C'est juste que n'avoir pas -Wnoà éteindre warn_no_constructor_for_refconst rend PITA utiliser Boost.ConceptCheck et similaires: github.com/boostorg/concept_check/blob/…
mloskot
2

Je n'utilise pas Clang, mais j'espère que mon opinion ne me dérange pas. J'utilise également le niveau d'avertissement maximal (je suppose que c'est ce que signifie -Wall) et de traiter les avertissements comme des erreurs (je suppose que c'est ce que signifie -Werror) et je ne désactive que les quelques avertissements qui n'ont pas beaucoup de sens. En fait, le fait que certains avertissements très utiles ne soient pas émis par le compilateur C # me contrarie énormément, et il est nécessaire d’exécuter des outils spéciaux d’analyse de code pour les visualiser. J'aime les avertissements, car ils m'aident à attraper de petites erreurs et des bugs dans mon code. Je les aime tellement que lorsqu'un avertissement est émis pour un élément de code que je sais être correct, je le reformule quand même de sorte que l'avertissement ne soit pas émis, plutôt que de le désactiver, ou - même pire, laissez-le apparaître et ignorez-le. Je pense que les avertissements sont géniaux,

Mike Nakis
la source
1
Merci d'avoir répondu. Malheureusement, -Wallne fournit que des avertissements de base. Beaucoup de drapeaux d'avertissement existants ne sont pas couverts -Wall, d'où la liste dans ma question.
Macmade
0

Je vais généralement avec les paramètres par défaut de Xcode pour un nouveau projet; il fournit un bon équilibre entre utile et ennuyeux. Toute liste spécifique d’avertissements et d’autres options du compilateur reposera en grande partie sur les préférences personnelles ou sur les exigences du projet. Je ne pense donc pas qu’il soit très utile d’essayer de parcourir votre liste et de plaider pour ou contre des avertissements spécifiques. Si cela fonctionne pour vous, tant mieux. Si vous trouvez que vous avez commis une erreur que le compilateur devrait pouvoir détecter et que vous souhaitez obtenir de l'aide à ce sujet à l'avenir, recherchez une option du compilateur pour vous en avertir et activez-la.

Certains programmeurs préconisent d'activer l'option "Traiter les avertissements comme des erreurs" (-Werror) pour empêcher les générations de se terminer avec succès s'il y avait des avertissements.

Caleb
la source
Merci d'avoir répondu! Je suis d'accord avec -Werror. En fait, comme j'utilise Clang, je mets tous ces avertissements en utilisant pragma (#pragma tintement de diagnostic), et ils sont mis à des erreurs fatales, c'est donc le même que -Werror:)
Macmade
0

Je pense que la meilleure preuve que les gens se soucient des avertissements est le fait qu'ils existent et sont largement utilisés. Je suis fermement convaincu que plus il y a d'erreurs détectées pendant la compilation, mieux c'est. Si cette croyance n'était pas largement répandue, tout le monde utiliserait des langages faiblement typés de manière dynamique, car leurs compilateurs ou interprètes vous donnent beaucoup plus de latitude. Au contraire, même sur les langages de script, l’utilisation des strictdrapeaux est populaire. Sur les langages statiques fortement typés, non seulement les gens utilisent -Wall -Werrorfréquemment, mais ils trouvent souvent les avertissements si précieux qu'ils en veulent encore plus , et paient donc pour des outils d'analyse statiques tels que Coverity, dont le seul but est de fournir des avertissements.

Cela ne signifie pas qu'il n'y a pas de détracteurs. Un grand nombre de développeurs considèrent que les avertissements vont à l’encontre de ceux-ci à court terme plutôt que de les défendre à long terme, et n’essaient pas de résoudre le problème sous-jacent. Par conséquent, vous voyez un code charmant comme ce bout de code que j'ai rencontré hier:

node = node; // Shut up compiler warning
Karl Bielefeldt
la source
Merci pour votre réponse. Le problème est que je vois souvent des personnes travailler sans -Werroret ignorer les avertissements émis par le compilateur. Ou s'ils le font, ils s'en tiennent généralement à -Wall. La plupart des drapeaux que je fournis dans ma question ne sont pas couverts -Wallet je pense personnellement qu'ils sont également essentiels. Alors je suis juste paranoïaque? ; )
Macmade
0

En règle générale, je me penche davantage vers -Wall et je crois vraiment y compris -Werror. Un module n'aurait jamais dû s'attendre à des avertissements. Vous finirez par recevoir un autre avertissement et le rater. Et ce sera celui qui pose problème.

Cela dépend également de l'ajout de "-Wall -Werror" à un nouveau ou à un ancien projet. Si c'est nouveau, allez-y et exigez la perfection, sinon vous risquez probablement des jours ou des semaines de modifications et de tests. Je viens de faire cela sur un projet plus ancien et un peu petit et j'ai passé deux ou trois jours à supprimer les avertissements. Je pense que le code est plus propre maintenant.

En d'autres termes, essayez et voyez.

Excité

Randy Stegbauer
la source
1
Si vous êtes uniquement préoccupé par -Wallet -Werror, je suppose que vous devriez relire la question. Beaucoup de drapeaux d'avertissement ne sont pas couverts par -Wall. Merci pour la réponse, de toute façon.
Macmade