Comment les compilateurs doivent-ils signaler les erreurs et les avertissements?


11

Je n'ai pas l'intention d'écrire un compilateur dans un avenir proche; malgré tout, je suis assez intéressé par les technologies de compilation et comment ces choses pourraient être améliorées.

À partir des langages compilés, la plupart des compilateurs ont deux niveaux d'erreur: les avertissements et les erreurs, le premier étant la plupart du temps des éléments non fatals que vous devez corriger, et des erreurs indiquant la plupart du temps qu'il est impossible de produire une machine (ou des octets). code de l'entrée.

Cependant, c'est une définition assez faible. Dans certains langages comme Java, certains avertissements sont tout simplement impossibles à supprimer sans utiliser la @SuppressWarningdirective. De plus, Java traite certains problèmes non fatals comme des erreurs (par exemple, du code inaccessible en Java déclenche une erreur pour une raison que j'aimerais savoir).

C # n'a pas les mêmes problèmes, mais il en a quelques-uns. Il semble que la compilation se déroule en plusieurs passes, et une passe échouant empêchera les autres passes de s'exécuter. Pour cette raison, le nombre d'erreurs que vous obtenez lorsque votre build échoue est souvent largement sous-estimé. Sur une seule exécution, il se peut que vous ayez deux erreurs, mais une fois que vous les aurez corrigées, vous en obtiendrez peut-être 26 nouvelles.

Creuser en C et C ++ montre simplement une mauvaise combinaison sur Java et les faiblesses de diagnostic de compilation de C # (bien qu'il soit peut-être plus précis de dire que Java et C # se sont contentés de la moitié des problèmes chacun). Certains avertissements devraient vraiment être des erreurs (par exemple, lorsque tous les chemins de code ne renvoient pas une valeur) et ils sont toujours des avertissements car, je suppose, au moment où ils ont écrit la norme, la technologie du compilateur n'était pas assez bonne pour faire ce genre de chèques obligatoires. Dans la même veine, les compilateurs vérifient souvent plus que la norme ne le dit, mais utilisent toujours le niveau d'erreur d'avertissement "standard" pour les résultats supplémentaires. Et souvent, les compilateurs ne signalent pas immédiatement toutes les erreurs qu'ils ont pu trouver; cela peut prendre quelques compiles pour se débarrasser de tous. Sans parler des erreurs cryptiques que les compilateurs C ++ aiment cracher,

Ajoutant maintenant que de nombreux systèmes de build sont configurables pour signaler les échecs lorsque les compilateurs émettent des avertissements, nous obtenons juste un mélange étrange: toutes les erreurs ne sont pas fatales mais certains avertissements devraient; tous les avertissements ne sont pas mérités mais certains sont explicitement supprimés sans autre mention de leur existence; et parfois tous les avertissements deviennent des erreurs.

Les langues non compilées ont toujours leur part de rapports d'erreurs de merde. Les fautes de frappe en Python ne seront pas signalées tant que le code ne sera pas réellement exécuté, et vous ne pourrez jamais vraiment supprimer plus d'une erreur à la fois car le script cessera de s'exécuter après en avoir rencontré une.

PHP, de son côté, a un tas de niveaux d'erreur plus ou moins importants et d' exceptions. Les erreurs d'analyse sont signalées une par une, les avertissements sont souvent si mauvais qu'ils devraient abandonner votre script (mais ne le font pas par défaut), les notifications montrent souvent de graves problèmes de logique, certaines erreurs ne sont vraiment pas assez graves pour arrêter votre script mais tout de même faire, et comme d'habitude avec PHP, il y a des choses vraiment étranges là-bas (pourquoi diable avons-nous besoin d'un niveau d'erreur pour les erreurs fatales qui ne sont pas vraiment fatales?, E_RECOVERABLE_E_ERRORje vous parle).

Il me semble que toutes les implémentations de rapports d'erreurs du compilateur auxquelles je peux penser sont cassées. Ce qui est vraiment dommage, car tous les bons programmeurs insistent sur l'importance de traiter correctement les erreurs et ne peuvent pas obtenir leurs propres outils pour le faire.

Selon vous, quelle devrait être la bonne façon de signaler les erreurs du compilateur?


-1: "Les langues non compilées ont toujours leur part de rapport d'erreurs de merde" Subjective et argumentative. Vraiment inutile. Est-ce une question ou une plainte?
S.Lott

2
@ S.Lott Je pense que vous êtes un peu à la traîne ici. Je trouve que j'étais beaucoup plus difficile sur les langues compilées, et cela ne semblait pas vous déranger.
zneak

@zneak: Les autres déclarations sont plus proches d'être factuelles et plus difficiles à analyser. Cette déclaration s'est révélée plus facilement subjective et argumentative.
S.Lott

1
@ S.Lott Ai-je tort de dire que Python indique une erreur à la fois?
zneak

1
@ S.Lott Ensuite, les choses doivent avoir changé, car la dernière fois que j'ai essayé, toute erreur de syntaxe ferait que Python arrêterait de "compiler" et une erreur de nom lèverait une exception et ne vérifierait pas le reste de la fonction (bien que cela ait laissé pour signaler une erreur par unité testable). Ma déclaration subjective et argumentative était une introduction à ce que je croyais être un fait, mais si ce n'est plus vrai, je vais modifier ma question. Comment ça marche maintenant?
zneak

Réponses:


6

Votre question ne semble pas concerner la manière dont nous signalons les erreurs du compilateur, mais plutôt la classification des problèmes et ce qu'il faut faire à leur sujet.

Si nous commençons par supposer, pour le moment, que la dichotomie avertissement / erreur est correcte, voyons comment nous pouvons construire en plus de cela. Quelques idées:

  1. Différents "niveaux" d'avertissement. Un grand nombre de compilateurs implémentent cela (par exemple, GCC a beaucoup de commutateurs pour configurer exactement ce qu'il avertira), mais cela nécessite du travail - par exemple, signaler la gravité d'un avertissement signalé et la possibilité de définir des "avertissements" sont des erreurs "uniquement pour les avertissements supérieurs à une gravité spécifiée.

  2. Classification saine des erreurs et des avertissements. Une erreur ne doit être signalée que si le code ne correspond pas à la spécification et ne peut donc pas être compilé. Les instructions inaccessibles, bien que probablement une erreur de codage, devraient être un avertissement , pas une erreur - le code est toujours "valide", et il existe des cas légitimes dans lesquels on voudrait compiler avec du code inaccessible (modifications rapides pour le débogage, par exemple) .

Maintenant, les choses sur lesquelles je ne suis pas d'accord:

  1. Faire des efforts supplémentaires pour signaler chaque problème. S'il y a une erreur, cela rompt la construction. Le build est cassé. La construction ne fonctionnera pas tant que cette erreur ne sera pas corrigée. Par conséquent, il est préférable de signaler cette erreur immédiatement, plutôt que de "continuer" afin d'essayer d'identifier tout le reste "mal" avec le code. Surtout quand beaucoup de ces choses sont probablement causées par l'erreur initiale de toute façon.

  2. Votre exemple spécifique d'un avertissement qui devrait être une erreur. Oui, c'est probablement une erreur de programmeur. Non, cela ne devrait pas casser la construction. Si je sais que l'entrée de la fonction est telle qu'elle retournera toujours une valeur, je devrais être en mesure d'exécuter la génération et de faire des tests sans avoir à ajouter ces vérifications supplémentaires. Oui, ce devrait être un avertissement. Et une putain de sévérité à cela. Mais cela ne devrait pas casser la construction en soi, sauf si la compilation avec des avertissements sont des erreurs.

Pensées?


Je suis d'accord avec vous, sauf pour les points où nous ne sommes pas d'accord (duh), donc c'est +1 de ma part. Je pense qu'il est assez facile de faire en sorte que chaque chemin de code retourne une valeur ou abandonne votre programme, compte tenu de sa gravité lorsque vous tombez réellement dans le cas du comportement non défini.
zneak

7

Un problème que vous avez évoqué était le signalement incomplet des erreurs - par exemple, le signalement de 2 erreurs, et lorsque vous les corrigez, vous obtenez un tas d'autres.

Il s'agit (en grande partie) d'un compromis de la part du rédacteur du compilateur. En fonction de l' erreur que vous avez fait, il est très facile pour le compilateur de commencer à se méprendre sur ce que vous n'avez assez mal qu'il commence à signaler les erreurs qui ont très peu à voir avec la réalité. Par exemple, considérons une simple faute de frappe où vous avez quelque chose comme au lieu de . Sauf si vous avez fait autre chose qui fait quelque chose de signifiant, cela va être signalé comme une erreur. C'est très bien dans la mesure où cela va, mais considérons maintenant ce qui se passe ensuite - le compilateur examine beaucoup de code qui essaie d' utiliser comme variable. Doit-il A) s'arrêter et vous laisser corriger cela, ou B) cracher 2000 erreurs ou quelque chose sur cette commande? Considérez une autre possibilité:itn x;int x;itn xerror: "x": undeclared identifier

int main()[

Ceci est une autre faute de frappe assez évidente - évidemment, ce devrait être un {au lieu d'un [. Le compilateur peut vous dire cette partie assez facilement - mais devrait-il ensuite signaler une erreur pour quelque chose comme x=1;dire quelque chose comme error: statement only allowed inside a function?

Notez que ce sont même des problèmes assez triviaux - des problèmes bien pires sont faciles à trouver (surtout, comme la plupart d'entre nous le savent, lorsque vous entrez dans des modèles C ++). L'essentiel est que le rédacteur du compilateur est généralement coincé à essayer de faire un compromis entre signaler des fausses erreurs (c'est-à-dire signaler quelque chose comme une erreur, même si c'est bien) et ne pas signaler de vraies erreurs. Il y a quelques règles de base que la plupart suivent pour essayer de ne pas aller trop loin dans les deux sens, mais presque aucune n'est presque parfaite.

Un autre problème que vous avez mentionné était Java et @SupressWarning. Ceci est assez différent de ce qui précède - il serait assez trivial de corriger. La seule raison pour laquelle ce n'est pas corrigé est que cela ne correspond pas au "caractère" de base de Java - c'est-à-dire, à leur avis, "ce n'est pas un bug, c'est une fonctionnalité". Même si c'est généralement une blague, dans ce cas, les personnes impliquées sont tellement malavisées qu'elles croient vraiment que c'est vrai.

Le problème que vous mentionnez en C et C ++ avec des chemins de code qui ne renvoient pas de valeur n'est pas vraiment pour permettre les compilateurs primitifs. C'est pour permettre des décennies de code existant , dont certains ne veulent pas réparer, toucher ou même lire. C'est ancien et laid mais ça marche, et personne ne veut rien d'autre que de continuer à fonctionner. Pour le meilleur ou pour le pire, les comités linguistiques sont à peu près obligés de maintenir cette compatibilité descendante, ils continuent donc à autoriser des choses que personne n'aime vraiment - mais certaines personnes (du moins pensent qu'elles en ont besoin).


3
En plus de votre point sur les erreurs précoces qui en provoquent de nombreuses autres, il y a aussi le fait que les passes ultérieures sont souvent construites pour exiger que les passes précédentes soient terminées avec succès. Par exemple, l'une des premières passes du compilateur C # vérifie qu'il n'y a pas de cycle dans le graphe d'héritage - vous n'avez pas A hérite de B qui hérite de A. Si vous voulez continuer et générer une liste de toutes les erreurs après cela, chaque passage ultérieur devrait être capable de faire face aux cycles - ce qui le rend nettement plus lent, même sur les «bonnes» compilations.
Anon.

@Anon. Le compilateur Java fait de bien meilleurs efforts pour survivre aux premières passes, et je ne le trouve pas beaucoup plus lent. Pour moi, c'est un peu ennuyeux à quel point cscabandonne rapidement .
zneak

@zneak: Comme le dit Jerry, c'est un compromis de la part des développeurs des compilateurs. Écrire de bons diagnostics d'erreur est en fait un problème très difficile (regardez clang pour voir jusqu'où vous pouvez vraiment aller). Voir ici pour une bonne discussion des phases et passes du compilateur C #.
Dean Harding le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.