web-dev-qa-db-fra.com

Indicateurs d'avertissement de Clang pour le développement d'Objective-C

En tant que programmeur C et 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 des indicateurs d'avertissement pour le compilateur que j'utilise, et j'allume 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 à être au courant de chaque petit détail, des problèmes potentiels d'implémentation et d'architecture, etc.

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

Pour la partie subjective de cette question, je suis intéressé à entendre d'autres développeurs (principalement C, Objective-C et C++) à ce sujet.
Vous souciez-vous réellement de choses comme les avertissements pédants, etc.? Et si oui ou non, pourquoi?

En ce qui concerne Objective-C, j'ai récemment complètement basculé vers la chaîne d'outils LLVM (avec Clang), au lieu de GCC.

Sur mon code de production, je place généralement ces drapeaux d'avertissement (explicitement, même si certains d'entre eux peuvent être couverts par -Wall):

  • -Mur
  • -Wbad-fonction-cast
  • -Wcast-align
  • -Wconversion
  • -Déclaration-après-déclaration
  • -Implémentations-dépréciées
  • -Wextra
  • -Wfloat-égal
  • -Wformat = 2
  • -Wformat-nonliteral
  • -Wfour-char-constants
  • -Propriétés atomiques implicites
  • -Attelles manquantes
  • -Déclarations manquantes
  • -Initialiseurs de champ manquants
  • -Attribut de format manquant
  • -Retour-noreturn
  • -Prototypes manquants
  • -Externes intéressés
  • -Wnewline-eof
  • -Définition de style ancien
  • -Chaînes de longueur d'onde
  • -Wparentheses
  • -Wpointer-arith
  • -Décrits-redondants
  • -Type de retour
  • -Point de séquence
  • -Wshadow
  • -Wshorten-64-à-32
  • -Wsign-comparer
  • -Wsign-conversion
  • -Prototypes Wstrict
  • -Accord-sélecteur-match
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • -Sélecteur déclaré
  • -Wuninitialized
  • -Pragmas-inconnus
  • - Code accessible
  • -Fonction inutilisée
  • -Étiquette non utilisée
  • -Paramètre-utilisé
  • -Valeur non utilisée
  • -Variable-variable
  • -Wwrite-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 souhaité), et pourquoi?

[~ # ~] modifier [~ # ~]

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

92
Macmade

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 plus grands utilisateurs des diagnostics de Clang, j'essaierai de faire la lumière sur ces indicateurs et comment 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.

Version TL; DR: veuillez utiliser -Wall et -Werror au minimum sur 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 détecte des bogues pour vous, activez-le également. Essayez -Wextra pour un tas de bons candidats ici. Si l'un d'eux est trop bruyant pour que vous puissiez l'utiliser de manière rentable, signalez un bug . Si vous écrivez du code contenant un bogue "évident" mais que le compilateur ne l'a pas averti, signalez un bogue.

Maintenant pour la version longue. Tout d'abord quelques informations sur les groupes de drapeaux d'avertissement. Il y a beaucoup de "groupements" d'avertissements à Clang (et dans une mesure limitée 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 élevé confiance en leur valeur et en un faible taux de faux positifs.
  • -Wextra: Ce sont des avertissements qui sont considérés comme valables et fiables (c'est-à-dire qu'ils ne sont pas bogués), mais ils peuvent avoir des taux de faux positifs élevés ou des objections philosophiques courantes.
  • -Weverything: Ceci est un groupe insensé qui active littéralement tous les avertissement dans Clang. Ne l'utilisez pas sur votre code. Il est strictement destiné aux développeurs Clang ou pour explorer quels avertissements existent.

Il y a deux critères principaux mentionnés ci-dessus qui guident où les avertissements vont dans Clang, et clarifions ce qu'ils signifient vraiment. Le premier est le potentiel valeur d'une occurrence particulière de l'avertissement. C'est l'avantage attendu pour l'utilisateur (développeur) lorsque l'avertissement se déclenche et correctement identifie un problème avec le code.

Le deuxième critère est l'idée de faux positifs rapports. Il s'agit de situations où l'avertissement se déclenche sur le code, mais le problème potentiel cité ne se produit pas en fait en raison du contexte ou d'une autre contrainte du programme. Le code averti se comporte en fait correctement. Ceux-ci sont particulièrement mauvais lorsque l'avertissement n'a jamais été conçu pour se déclencher sur ce modèle de code. Au lieu de cela, c'est une lacune dans la mise en œuvre de l'avertissement qui le fait se déclencher là-bas.

Pour les avertissements Clang, la valeur doit être en termes de exactitude, pas en termes de style, de goût ou de conventions de codage. Cela limite l'ensemble des avertissements disponibles, excluant les avertissements souvent demandés tels que les avertissements chaque fois que {}s ne sont pas utilisés autour du corps d'une instruction if. Clang est également très intolérant envers faux positifs. Contrairement à la plupart des autres compilateurs, il utilisera une incroyable variété de sources d'informations pour élaguer les faux positifs, y compris l'orthographe exacte de la construction, la présence ou l'absence de "()" supplémentaires, les transtypages ou même les macros de préprocesseur!

Prenons maintenant des exemples d'avertissements du monde réel de Clang et regardons comment ils sont classés. 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, donnant l'avertissement élevé valeur, et l'avertissement ne se déclenche que sur le code que Clang peut prouver tombe dans ce compartiment, ce qui lui donne un zéro false -positive taux.

% 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 nécessite le -Wall indicateur pour cet avertissement. La raison en est qu'il existe une quantité de code non triviale qui a utilisé (pour le meilleur ou pour le pire) le modèle de code que nous vous avertissons de intentionnellement produire une valeur non initialisée. D'un point de vue philosophique, je ne vois aucun intérêt à cela, mais beaucoup d'autres ne sont pas d'accord et la réalité de cette différence d'opinion est ce qui motive l'avertissement sous le -Wall drapeau. Il a toujours un taux valeur très élevé et un taux false-positive très faible, mais sur certaines bases de code, il ne s'agit pas d'un démarreur.

% 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 requiert le -Wextra drapeau. La raison en est qu'il existe très de grandes bases de code où les comparaisons de connexion 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 assez faible en moyenne. Le résultat est un taux faux-positif extrêmement élevé. Cependant, quand il y a un bug dans un programme à cause des règles de promotion étranges, il est souvent extrêmement subtil de faire cet avertissement quand il signale un bug ont relativement élevé valeur . En conséquence, Clang le fournit et l'expose sous un drapeau.

En règle générale, les avertissements ne vivent pas longtemps en dehors du -Wextra drapeau. Clang essaie très fort de ne pas implémenter les avertissements qui ne voient pas une utilisation et des tests réguliers. Les avertissements supplémentaires activés par -Weverything sont généralement des avertissements en cours de développement actif ou avec des bogues actifs. Soit ils seront fixés et placés sous des drapeaux appropriés, soit ils devront être retirés.

Maintenant que nous comprenons comment ces choses fonctionnent avec Clang, essayons de revenir à la question initiale: quels avertissements devez-vous activer pour votre développement? La réponse est, malheureusement, que cela dépend. Tenez compte des questions suivantes pour déterminer les avertissements qui conviennent le mieux à votre situation.

  • Avez-vous le contrôle sur tout votre code, ou une partie est-elle externe?
  • Quels sont vos objectifs? Attraper des bogues ou écrire un meilleur code?
  • Quelle est votre tolérance aux faux positifs? Êtes-vous prêt à écrire du code supplémentaire pour faire taire régulièrement les avertissements?

Tout d'abord, si vous ne contrôlez pas le code, n'essayez pas d'activer des avertissements supplémentaires. Soyez prêt à en désactiver. Il y a beaucoup de mauvais code dans le monde et vous ne pourrez peut-être pas tout réparer. Ça va. Essayez de trouver un moyen de concentrer vos efforts sur le code vous contrôle.

Ensuite, déterminez ce que vous voulez de vos avertissements. C'est différent pour différentes personnes. Clang essaiera d'avertir sans aucune option sur les bogues flagrants, ou les modèles de code pour lesquels nous avons un long précédent historique indiquant que le taux de bogues est extrêmement élevé. En activant -Wall vous 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 les deux, le taux faux positif devrait rester assez bas.

Enfin, si vous êtes parfaitement disposé à faire taire * faux positif * s à chaque tour, optez pour -Wextra. Fichier bogues si vous remarquez des avertissements qui attrapent beaucoup de vrais bogues, mais qui ont des faux positifs stupides ou inutiles. Nous travaillons constamment pour trouver des moyens d'apporter de plus en plus de logique de recherche de bogues présente dans -Wextra en -Wall où nous pouvons éviter les faux positifs.

Beaucoup trouveront qu'aucune de ces options ne leur convient parfaitement. Chez Google, nous avons transformé certains avertissements en -Wall off en raison de nombreux codes existants qui ont violé l'avertissement. Nous avons également activé certains avertissements de manière explicite, 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 manière similaire. Il est souvent préférable d’activer quelques avertissements clés plutôt que tous -Wextra.

J'encourage tout le monde à activer -Wall pour tout code non hérité. Pour le nouveau code, les avertissements ici sont presque toujours utiles et améliorent vraiment l'expérience de développement de code. Inversement, j'encourage tout le monde à pas activer les drapeaux au-delà de -Wextra. Si vous trouvez un avertissement Clang que -Wextra ne comprend pas mais qui prouve pas du tout précieux pour vous, il suffit de déposer un bogue et nous pouvons probablement le mettre sous -Wextra. Si vous activez explicitement un sous-ensemble des avertissements dans -Wextra dépendra fortement de votre code, de votre style de codage et de la question de savoir si le maintien de cette liste est plus facile que de tout réparer découvert par -Wextra.

De la liste des avertissements du PO (qui comprenait à la fois -Wall et -Wextra) seuls les avertissements suivants sont pas couverts par ces deux groupes (ou activés par défaut). Le premier groupe souligne pourquoi une dépendance excessive à l'égard des indicateurs d'avertissement explicites peut être mauvaise: aucun d'entre eux 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

Le prochain ensemble d'avertissements inutiles dans la liste d'origine sont ceux qui sont redondants avec d'autres dans 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 qui sont plus catégoriquement différents. Celles-ci traitent des variantes de dialecte linguistique plutôt que du code bogué ou non bogué. À l'exception de -Wwrite-strings, ce sont tous des avertissements pour les extensions de langue fournies par Clang. Le fait que Clang prévienne ou non de leur utilisation dépend de la prévalence de l'extension. Clang vise la compatibilité GCC, et donc dans de nombreux cas, il facilite cela avec des extensions de langage implicites qui sont largement utilisées. -Wwrite-strings, comme commenté sur l'OP, est un indicateur de compatibilité de GCC qui change réellement la sémantique du programme. Je regrette profondément ce drapeau, mais nous devons le soutenir en raison de son héritage actuel.

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

Les options restantes qui activent réellement 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 ceux-ci ne sont pas dans -Wall ou -Wextra n'est pas toujours clair. Pour beaucoup d'entre eux, ils sont en fait basés sur des avertissements GCC (-Wconversion, -Wshadow, etc.) et en tant que tel, Clang essaie d'imiter le comportement de GCC. Nous décomposons lentement certains d'entre eux en avertissements plus fins et utiles. Ceux-ci ont alors une probabilité plus élevée d'en faire l'un des groupes d'avertissement de niveau supérieur. Cela dit, pour reprendre un avertissement, -Wconversion est donc large qu'il restera probablement sa propre catégorie de "haut niveau" dans un avenir prévisible. Certains autres avertissements que GCC a mais qui ont une faible valeur et des taux de faux positifs élevés peuvent être relégués dans un no man's land similaire.

D'autres raisons pour lesquelles ceux-ci ne figurent pas dans l'un des plus grands compartiments incluent les bogues simples, les problèmes de faux positifs très importants 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 drapeau de grand seau approprié ou être supprimés de Clang.

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

165
Chandler Carruth

Je n'utilise pas Clang, mais j'espère que cela ne vous dérangera pas d'avoir mon avis aussi. Je vais également avec le niveau d'avertissement maximum (je suppose que c'est ce que signifie -Wall) et en traitant les avertissements comme des erreurs (je suppose que c'est ce que -Werror signifie) et je ne désactive que les quelques avertissements qui n'ont pas beaucoup de sens. En fait, je suis assez ennuyé par le fait que certains avertissements très utiles ne sont pas émis par le compilateur C #, et il faut exécuter des outils d'analyse de code spéciaux pour les voir. J'aime les avertissements, car ils m'aident à détecter les petites erreurs et bugs dans mon code. Je les aime tellement, que lorsqu'un avertissement est émis pour un morceau de code que je connais est correct, je refactorise ce morceau de code de toute façon, afin que l'avertissement ne soit pas émis, plutôt que de désactiver l'avertissement, ou --even pire - laissez-le apparaître et ignorez-le. Je pense que les avertissements sont géniaux, et j'aimerais qu'il y en ait plus.

3
Mike Nakis

En général, je penche davantage vers -Wall, et je crois vraiment qu'il faut également inclure -Werror. Un module devrait jamais avoir des avertissements attendus. Vous finirez par recevoir un autre avertissement et vous le manquerez. Et ce sera celui qui posera réellement problème.

Cela dépend également si vous ajoutez "-Wall -Werror" à un nouveau ou un ancien projet. Si c'est nouveau, alors allez-y et exigez la perfection, sinon vous risquez probablement des jours ou des semaines de modifications et de tests. Je viens de le faire sur un projet un peu plus ancien 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-le et voyez.

Excité

1
Randy Stegbauer

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 d'avis que plus il y a d'erreurs détectées lors de la compilation, mieux c'est. Si ce n'était pas une croyance répandue, tout le monde utiliserait des langages typés dynamiquement faibles, car leurs compilateurs ou interprètes vous donnent beaucoup plus de latitude. Au contraire, même sur les langages de script, l'utilisation des drapeaux strict est populaire. Sur les langues statiques fortement typées, non seulement les gens utilisent -Wall -Werror fréquemment, mais ils trouvent souvent les avertissements si précieux qu'ils en veulent encore plus , donc ils paient pour des outils d'analyse statique comme Coverity = dont le seul but est de fournir des avertissements.

Cela ne veut pas dire qu'il n'y a pas de détracteurs. De nombreux développeurs considèrent que les avertissements agissent contre eux à court terme plutôt que de travailler pour eux à long terme, et n'essaient pas de résoudre le problème sous-jacent. Par conséquent, vous voyez un joli code comme cet extrait que j'ai rencontré hier:

node = node; // Shut up compiler warning
1
Karl Bielefeldt

J'utilise généralement les paramètres par défaut de Xcode pour un nouveau projet; il offre un bon équilibre entre utile et ennuyeux. Toute liste spécifique d'avertissements et d'autres options du compilateur sera basée en grande partie sur les préférences personnelles ou les exigences du projet, donc je ne pense 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 fait une erreur que le compilateur devrait être en mesure de détecter et que vous souhaitez de l'aide à ce sujet à l'avenir, recherchez une option de compilateur pour vous en avertir et activez-la.

De nombreux programmeurs préconisent d'activer l'option "Traiter les avertissements comme des erreurs" (-Werror) pour empêcher les builds de se terminer avec succès s'il y a des avertissements.

1
Caleb