Section courante

A propos

Section administrative du site

Vivre avec Lint

Lorsque vous appliquerez pour la première fois Lint à un programme C volumineux n'ayant jamais été analysé auparavant, vous recevrez sans doute bien plus de messages que prévu. Vous ressentirez peut-être la même chose que les concepteurs de PC-Lint lorsqu'ils ont exécuté PC-Lint pour la première fois sur un de leurs programmes et qu'ils ont constaté qu'il rejetait du code C parfaitement valide : ils avait envie d'écrire en C, et non dans PC-Lint.

Les récits de l'efficacité de Lint sont, en revanche, légendaires. PC-Lint a bien sûr été testé sur lui-même et, malgré des tests préalables exhaustifs, plusieurs erreurs subtiles ont été révélées. Ils ont testé une commande grep du domaine public qu'il n'avait jamais osé utiliser car elle plantait mystérieusement. PC-Lint a trouvé le problème : un pointeur non initialisé.

Il est non seulement nécessaire de tester un programme une seule fois, mais aussi de le tester en continu tout au long de son développement et de sa maintenance.

Ils ont consacré plusieurs semaines à traquer un bogue que PC-Lint aurait facilement détecté (une incompatibilité de type d'argument). Ils ne seront plus jamais tentés de déboguer avant d'avoir effectué une analyse statique du code.

Mais comment gérer la multitude de messages d'erreur ? Faire le tri peut s'avérer fastidieux, surtout si c'est une tâche constante. La meilleure solution consiste à définir une politique (pouvant initialement être assez souple) concernant les messages que l'on peut ignorer. Par exemple, on peut désactiver tous les messages de niveau 700 (messages d'information) avec l'option `-e7??`. Il suffit ensuite de corriger les erreurs associées aux messages restants.

Cette politique peut être appliquée automatiquement en intégrant les options de suppression d'erreurs dans un fichier de traitement par lot et/ou `.lnt` (voir exemples ci-dessous) et renforcée progressivement avec le temps et l'expérience.

Exemple de politique

Voici un exemple de pratiques que les concepteurs on pu eux-même adopter sans problème. (Le texte est rédigé à la troisième personne afin de bien montrer qu'il ne s'agit que d'une politique parmi d'autres qui pourraient être raisonnablement suivies.)

Il utilise fréquemment la capacité du langage C à tester le résultat d'une affectation en utilisant une construction telle que :

  1. if (a = value )
  2.     { ...

Il supprime donc systématiquement l'erreur 720 grâce à l'option :

-e720

Un jour, s'il en a le temps et s'il est convaincu qu'il ne perdra pas en efficacité, il pourrait convertir tout cela en :

  1. if( (a = value) != 0)
  2.     { ...

Mais pour l'instant, il prendra le risque.

Notez cependant que les anciens programmeurs Pascal devraient peut-être privilégier cette dernière solution et ne pas inhiber 720.

Une autre option de suppression d'erreurs qu'il utilise régulièrement est `-epp`, empêchant les erreurs de pointeur-pointeur. Si, par exemple, `p` est un pointeur vers une structure, cela lui permet d'écrire :

  1. p = malloc(n);

sans avoir à convertir le pointeur. Cela évite bien des tracas lors de la relecture de l'instruction pendant le débogage. Sur une machine à adressage par mot, il est crucial de bien distinguer les pointeurs de type caractère des autres pointeurs, mais sur toutes les machines où le code est susceptible de s'exécuter aujourd'hui, les pointeurs de différents types sont interchangeables. Si le code devait s'exécuter sur une machine à adressage par mot, il pourrait (et devrait) rétablir la vérification d'erreur.

Autre exemple de son style de programmation particulier : il mélangeait sans discernement les nombres non signés et signés. C'est pourquoi il utilise les options de suppression des messages.

-eau -e502 -e713

(L'erreur 502 concerne l'application d'un tiret à une quantité signée, l'erreur 713 concerne l'affectation d'une valeur non signée à une valeur signée, et l'option `-e718` supprime les messages d'erreur liés à la discordance entre un argument et un paramètre, l'un étant signé et l'autre non signé).

Il utilise fréquemment des fonctions non déclarées au préalable, ce qui leur permet de prendre par défaut le type `int` (ou `void` si elles ne semblent pas renvoyer de valeur). Par conséquent, il utilise fréquemment l'option `-e718` (fonction non déclarée).

Il estime que s'il lance un sort, il sait ce qu'il fait et Lint ne devrait pas se plaindre, alors il supprime systématiquement l'erreur numéro 711 (perte de précision liée à un sort).

Il n'apprécie pas la hauteur par défaut de 4 pour les messages d'erreur et préfère une hauteur plus compacte (comme 2). Il utilise donc l'option -h2.

Il place la liste d'options favorites pour la suppression des erreurs dans un fichier nommé options.1. Voici à quoi il ressemble :

  1. /*lint -e502 -e713 -eau  unsigned-signed */
  2. /*lint -e720   test of assignement *.
  3. /*lint -e711   lose of precision in a cast */
  4. /*lint -epp    pointers are pointers */
  5. /*lint -e715   formal parameter not referenced */
  6. /*lint -e718   undeclared function */
  7. /*lint -h2     error message height */

Il s'agit en réalité d'un module C nul plutôt que d'un fichier indirect (.lnt). L'avantage de les placer dans un module C nul est double. Premièrement, les options peuvent être commentées comme indiqué. Deuxièmement, cela offre une plus grande flexibilité car elles peuvent alors être appelées depuis différents endroits : la ligne de commande, un fichier indirect, un autre module (via la fonction d'inclusion),...

Il placerai la référence à option.1 ainsi qu'une référence à la bibliothèque standard de son choix dans un fichier indirect nommé std.lnt. Voici à quoi cela ressemble :

options.1 stdlib.c

Il fait ensuite référence à std.lnt depuis un fichier de traitement par lots, comme décrit ci-dessous. L'avantage de cette double indirection est que, pour des projets spécifiques, il utilisera un fichier std.lnt particulier pouvant inclure des options supplémentaires par rapport à celles de options.1, ainsi que d'autres fichiers de bibliothèque, potentiellement différents. Tout ceci doit être réalisé à l'aide du même fichier de traitement par lots de base.

Il accède généralement à PC-Lint via un fichier de traitement par lots nommé lin.bat contenant les éléments suivants :

if "%1" == "-prev" goto prev
lint +vm std.lnt %1 %2 %3 %4 %5 %6 ... >temp
:prev
type temp | more

Pour exécuter PC-Lint sur les fichiers m1.c à m6.c, on taperait ensuite :

lin m1 m2 m3 m4 m5 m6

L'exécution de Lint de cette manière enregistre les messages d'erreur dans un fichier étant affiché écran par écran dans la console. L'option +vm garantit que les messages en cours de traitement sont également affichés à l'écran et enregistrés dans le fichier. Pour consulter à nouveau toutes les erreurs, il suffit de taper :

lin -prev

Comme les erreurs sont enregistrées dans un fichier, il peut utiliser n'importe quel éditeur multifichier et alterner entre le fichier de messages d'erreur (temporaire) et un fichier source pour corriger les erreurs.

Les projets de grande envergure nécessitent souvent des options et des bibliothèques spécifiques. On souhaite également simplifier le traitement de tous les fichiers d'un projet, ainsi que de tout sous-ensemble. Par exemple, supposons un projet composé des modules m1.c, m2, m3.c, m4.c et m5.c, utilisant une bibliothèque décrite dans projlib.c (sous une forme similaire à stdlib.c) et le modèle de mémoire étendu. Dans ce cas, std.lnt peut devenir :

-mL options.1 stdlib.c projlib.c

Supposons que projects.lnt contienne :

m1 m2 m3 m4 m5

Alors :

lin -u m2 m4

L'option -u signifie «vérification unitaire» et supprime simplement les messages d'erreur qui ne sont pertinents que si tous les modules sont fournis.

Cela soulève la question de la gestion du fichier std.lst. Il est impératif d'éviter de devoir le modifier à chaque utilisation de PC-Lint dans un contexte différent. L'idéal serait un fichier std.lst situé à un emplacement global, masqué ou surchargé par le contexte local (par exemple, le répertoire du projet mentionné précédemment).

Il existe des utilitaires permettant de gérer les fichiers de la même manière que la variable d'environnement PATH de DOS gère les commandes (il utilise le programme SCOUT de Computer Insights, PO Box 110097, Pittsburgh, PA 15232, au prix de 29 $, mais d'autres existent). Cette solution est parfaitement adaptée.

Autre possibilité : utiliser l'option -i de PC-Lint dans le fichier de traitement par lots. Ainsi, si le fichier de traitement par lots contient :

lint -ic:\lint\ std.lst ...

Si les fichiers std.lnt, options.l, stdlib.c, etc. sont introuvables dans le répertoire courant, ils seront recherchés dans un répertoire central (dont le nom suit l'option -i).

En résumé, établissez des procédures permettant d'accéder facilement à l'outil de lint pour diverses applications. Testez lint par petites portions de votre projet avant de traiter l'ensemble. Définissez une politique de suppression des messages d'erreur, initialement assez souple, qui pourra être renforcée ultérieurement.



Dernière mise à jour : Lundi, le 6 avril 2026