コンパイラはどのようにエラーと警告を報告すべきですか?


11

私は近い将来にコンパイラを書くつもりはありません。それでも、私はコンパイラ技術に非常に興味があり、このようなものをどのように改善することができますか。

コンパイルされた言語から、ほとんどのコンパイラには2つのエラーレベルがあります。最初はほとんどの場合、修正する必要のある致命的ではない警告とエラー、およびマシン(またはバイト)入力からのコード。

しかし、これはかなり弱い定義です。Javaのような一部の言語では、@SuppressWarningディレクティブを使用せずに特定の警告を取り除くことは不可能です。また、Javaは致命的ではない特定の問題をエラーとして扱います(たとえば、Javaの到達不能コードは、知りたい理由でエラーをトリガーします)。

C#には同じ問題はありませんが、いくつかあります。コンパイルは複数のパスで発生し、パスが失敗すると以降のパスは実行されないようです。そのため、ビルドが失敗したときに取得するエラーカウントは、しばしば大幅に過小評価されます。1回の実行で2つのエラーがあると表示される場合がありますが、修正すると26の新しいエラーが発生する可能性があります。

CとC ++を掘ると、JavaとC#のコンパイル診断の弱点の悪い組み合わせが示されるだけです(ただし、JavaとC#はそれぞれ半分の問題で進んだと言う方が正確かもしれません)。いくつかの警告は実際にはエラーである必要があり(たとえば、すべてのコードパスが値を返すわけではない場合)、それでも警告です。標準を書いた時点では、コンパイラテクノロジはこれらの種類の必須チェック。同じように、コンパイラは多くの場合、標準で規定されている以上のことをチェックしますが、追加の検出には「標準」の警告エラーレベルを使用します。また、多くの場合、コンパイラはすぐに検出できるすべてのエラーを報告しません。それらをすべて削除するには、数回のコンパイルが必要になる場合があります。C ++コンパイラーが吐き出すのを好む不可解なエラーは言うまでもありませんが、

コンパイラーが警告を発したときに失敗を報告するように多くのビルドシステムを構成できるようになったので、奇妙なミックスが得られます。すべての警告に値するわけではありませんが、警告の存在をさらに言及せずにいくつかの警告を明示的に抑制しています。また、すべての警告がエラーになる場合があります。

コンパイルされていない言語には、依然としてエラー報告があります。Pythonのタイプミスは、コードが実際に実行されるまで報告されません。また、スクリプトがエラーを検出すると実行が停止するため、一度に複数のエラーをキックすることはできません。

PHPには、多かれ少なかれ重要なエラーレベル例外がたくさんあります。解析エラーは一度に1つずつ報告され、警告はスクリプトを中断するほどひどい場合があります(ただし、デフォルトではそうではありません)。 PHPの場合と同じように、本当に奇妙なことがあります(なぜ致命的ではない致命的なエラーのエラーレベルが必要なのでしょうか?E_RECOVERABLE_E_ERROR、私はあなたに話しています)。

私が考えることができるコンパイラエラーレポートのすべての実装が壊れているように思えます。すべての優秀なプログラマーが、エラーに適切に対処することの重要性を主張しているにもかかわらず、それを行うための独自のツールを入手できないため、これは本当に残念です。

コンパイラエラーを報告する正しい方法は何だと思いますか?


-1:「コンパイルされていない言語には、いまだにエラー報告があります」主観的かつ議論的。本当に役に立たない。これは質問ですか、苦情ですか?
S.Lott

2
@ S.Lottあなたはここの端にいると思います。コンパイルされた言語の方がずっと難しく、気にしないようです。
zneak

@zneak:他のステートメントは事実に近く、解析が困難です。その声明は、主観的かつ議論的であることが最も簡単に示されました。
-S.ロット

1
@ S.Lott Pythonが一度に1つのエラーを示すと言っているのは間違っていますか?
zneak

1
@ S.Lottそれから状況は変わったにちがいありません。前回試したとき、構文エラーが発生するとPythonが「コンパイル」の試行を停止し、名前エラーが例外をスローし、残りの関数をチェックしませんでしたテスト可能なユニットごとに1つのエラーを報告するための部屋)。私の主観的で議論的な発言は、私が事実だと信じていたものの紹介でしたが、もしそれが真実でなければ、質問を編集しに行きます。今どのように機能しますか?
-zneak

回答:


6

あなたの質問は、実際にはコンパイラエラーをどのように報告するかに関するものではないようです。むしろ、問題の分類とその対処方法に関するものです。

とりあえず、警告/エラーの二分法が正しいと仮定することから始めたら、その上にどれだけうまく構築できるか見てみましょう。いくつかのアイデア:

  1. さまざまな「レベル」の警告。多くのコンパイラがこれを実装しています(たとえば、GCCは警告する内容を正確に設定するためのスイッチを多数持っています)が、作業が必要です-たとえば、報告された警告の重大​​度を報告し、「警告指定された重大度を超える警告に対してのみエラーです。

  2. エラーと警告の健全な分類。エラーが報告されるのは、コードが仕様を満たしていないためコンパイルできない場合のみです。到達不能ステートメントはおそらくコーディングエラーですが、エラーではなく警告である必要があります-コードはまだ「有効」であり、到達不能コードでコンパイルしたい正当なインスタンスがあります(たとえば、デバッグのための迅速な変更) 。

今、私はあなたに同意しないもの:

  1. すべての問題を報告するために余分な努力をします。エラーがある場合、ビルドが中断されます。ビルドが壊れています。そのエラーが修正されるまで、ビルドは機能しません。したがって、他のすべてのコードが「間違っている」ことを確認するために、「続行」するよりも、そのエラーをすぐに報告する方が適切です。特に、それらの多くがおそらく最初のエラーが原因である可能性が高い場合。

  2. エラーになるはずの警告の具体例。はい、おそらくプログラマーの間違いです。いいえ、ビルドを壊さないでください。関数への入力が常に値を返すようなものであることがわかっている場合、ビルドを実行し、これらの余分なチェックを追加せずにいくつかのテストを実行できるはずです。はい、警告である必要があります。そして、それは非常に重大度の高いものです。ただし、warnings-are-errorsを使用してコンパイルしない限り、ビルド自体を壊してはなりません。

考え?


同意しない点(duh)を除き、私はあなたに同意します。未定義の振る舞いの場合に実際にどのくらい悪いのかを考えると、すべてのコードパスが値を返すかプログラムを中止するのは十分簡単だと思います。
zneak

7

あなたが提起した問題の1つは、エラーの不完全な報告です。たとえば、2つのエラーを報告し、それらを修正すると、さらに多くのエラーが発生します。

これは(大部分)コンパイラライター側の妥協です。コンパイラはあなたが何を勘違いして起動するためにあなたが作ったものをエラーに応じて、それは非常に簡単だひどく十分なそれは非常に少ない現実をどうするを持ってエラーを報告するために開始することがあります。たとえば、のitn x;代わりにのような単純なタイプミスがあるとしますint x;。他に何かitn意味のあることをしていない限り、これはエラーとして報告されます。それはそれでいい限りですが、次に何が起こるか考えてみましょう-コンパイラは変数として使用 しようとする多くのコードを調べxます。A)停止して修正するか、B)2000件のエラーerror: "x": undeclared identifierまたはその注文に関する何かを吐き出す必要がありますか?別の可能性を検討してください:

int main()[

これは、別のかなり明白タイプミスです-明らかにそれがあるべき{代わりに[。コンパイラーはその部分を非常に簡単に伝えることができます-しかし、その後、次のようなことをx=1;言ってエラーを報告する必要がありerror: statement only allowed inside a functionますか?

これらはかなり些細な問題でさえあることに注意してください-最悪の問題は簡単に見つけることができます(特に、私たちのほとんどが知っているように、C ++テンプレートを使用する場合)。結論としては、コンパイラの作成者は通常、誤ったエラーの報告(つまり、何か問題があったとしても、それは問題ありません)と実際のエラーの報告に失敗することの間で妥協しようとします。ほとんどの場合、どちらの方向にも行き過ぎないようにするための経験則がいくつかありますが、完璧に近いものはほとんどありません。

あなたが言及したもう1つの問題は、Javaと@SupressWarningでした。これは上記とはまったく異なります。修正するのはかなり簡単です。修正されない唯一の理由は、それがJavaの基本的な「キャラクター」に適合しないということです。つまり、彼らの意見では、「それはバグではなく、機能です」。通常は冗談ですが、この場合、関係者は誤った方向に導かれるため、それが本当だと本当に信じています。

CやC ++で言及している、値を返さないコードパスの問題は、実際にはプリミティブコンパイラを許可するものではありません。それは、何十年もの既存のコードを許可することであり、そのいくつかは誰も修正したり、触れたり、読んだりしたくないものです。古くていですが、機能します。動作を継続すること以外は誰も望んでいません。良くも悪くも、言語委員会はその下位互換性を維持することに固執しているので、誰も本当に好きではないが、一部の人々(少なくとも彼らは必要だと思う)を許可し続けています。


3
他の多くの原因となる早期エラーに関するあなたの主張に加えて、後のパスがしばしば構築されて、前のパスが正常に完了することを要求するという事実もあります。たとえば、C#コンパイラの初期パスの1つでは、継承グラフにサイクルがないことを確認します。Aから継承するAからBへの継承がないことを確認します。その後のすべてのエラーのうち、以降の各パスはサイクルに対処できる必要があります。つまり、「良好な」コンパイルでも大幅に遅くなります。
アノン。

@アノン。Javaコンパイラーは、初期のパスを生き抜くためにはるかに優れた努力を払っていますが、それほど遅いとは思いません。私にとっては、cscあきらめるのが少し面倒です。
zneak

@zneak:Jerryが言うように、それはコンパイラの開発者側の妥協です。優れたエラー診断を書くことは、実際には非常に難しい問題です(実際にどの程度実行できるかの例については、clangを参照してください)。C#コンパイラのフェーズとパスの詳細については、こちらご覧ください
ディーンハーディング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.