Bagaimana kompiler melaporkan kesalahan dan peringatan?

11

Saya tidak berencana menulis kompiler dalam waktu dekat; tetap, saya cukup tertarik dengan teknologi kompiler, dan bagaimana hal ini dapat dibuat lebih baik.

Dimulai dengan bahasa yang dikompilasi, sebagian besar kompiler memiliki dua tingkat kesalahan: peringatan dan kesalahan, yang pertama adalah sebagian besar waktu hal-hal non-fatal yang harus Anda perbaiki, dan kesalahan yang menunjukkan sebagian besar waktu bahwa tidak mungkin untuk menghasilkan mesin- (atau byte-) kode dari input.

Padahal, ini adalah definisi yang cukup lemah. Dalam beberapa bahasa seperti Java, peringatan tertentu tidak mungkin dihilangkan tanpa menggunakan @SuppressWarningarahan. Juga, Java memperlakukan masalah non-fatal tertentu sebagai kesalahan (misalnya, kode yang tidak terjangkau di Jawa memicu kesalahan karena alasan yang ingin saya ketahui).

C # tidak memiliki masalah yang sama, tetapi memang memiliki beberapa. Tampaknya kompilasi terjadi dalam beberapa lintasan, dan lintasan yang gagal akan membuat operan lebih lanjut tidak dieksekusi. Karena itu, jumlah kesalahan yang Anda dapatkan ketika bangunan gagal sering terlalu diremehkan. Sekali jalan mungkin mengatakan Anda memiliki dua kesalahan, tetapi begitu Anda memperbaikinya mungkin Anda akan mendapatkan 26 kesalahan baru.

Menggali ke C dan C ++ hanya menunjukkan kombinasi yang buruk pada kelemahan diagnostik kompilasi Java dan C # (meskipun mungkin lebih akurat untuk mengatakan bahwa Java dan C # berjalan sesuai dengan masing-masing masalah). Beberapa peringatan seharusnya merupakan kesalahan (misalnya ketika tidak semua jalur kode mengembalikan nilai) dan masih merupakan peringatan karena, saya kira, pada saat mereka menulis standar, teknologi kompiler tidak cukup baik untuk membuat semacam ini. memeriksa wajib. Dalam nada yang sama, kompiler sering memeriksa lebih dari standar mengatakan, tetapi masih menggunakan tingkat kesalahan peringatan "standar" untuk temuan tambahan. Dan seringkali, kompiler tidak akan melaporkan semua kesalahan yang dapat mereka temukan segera; mungkin butuh beberapa kompilasi untuk menyingkirkan semuanya. Belum lagi kesalahan cryptic compiler C ++ suka meludah,

Sekarang menambahkan bahwa banyak sistem build dapat dikonfigurasi untuk melaporkan kegagalan ketika kompiler mengeluarkan peringatan, kami hanya mendapatkan campuran aneh: tidak semua kesalahan fatal tetapi beberapa peringatan harus; tidak semua peringatan layak tetapi ada yang secara eksplisit ditekan tanpa menyebutkan keberadaannya lebih lanjut; dan terkadang semua peringatan menjadi kesalahan.

Bahasa yang tidak dikompilasi masih memiliki bagian pelaporan kesalahan buruk. Kesalahan ketik di Python tidak akan dilaporkan sampai kode benar-benar dijalankan, dan Anda tidak pernah dapat benar-benar melakukan lebih dari satu kesalahan pada satu waktu karena skrip akan berhenti dijalankan setelah bertemu satu.

PHP, di sisi lain, memiliki banyak tingkat kesalahan yang signifikan, dan pengecualian. Kesalahan parse dilaporkan satu per satu, peringatan sering kali sangat buruk sehingga harus membatalkan skrip Anda (tetapi jangan secara default), pemberitahuan benar-benar sering menunjukkan masalah logika yang serius, beberapa kesalahan sebenarnya tidak cukup buruk untuk menghentikan skrip Anda tetapi masih lakukan, dan seperti biasa dengan PHP, ada beberapa hal yang sangat aneh di sana (kenapa kita perlu tingkat kesalahan untuk kesalahan fatal yang tidak benar-benar fatal?, E_RECOVERABLE_E_ERRORsaya berbicara dengan Anda).

Tampak bagi saya bahwa setiap implementasi tunggal pelaporan kesalahan kompiler yang dapat saya pikirkan rusak. Ini benar-benar memalukan, karena bagaimana semua programmer yang baik menekankan betapa pentingnya menangani kesalahan dengan benar dan belum bisa mendapatkan alat mereka sendiri untuk melakukannya.

Menurut Anda apa cara yang tepat untuk melaporkan kesalahan kompiler?

zneak
sumber
-1: "Bahasa yang tidak dikompilasi masih memiliki bagian pelaporan kesalahan jelek" Subyektif dan argumentatif. Benar-benar tidak membantu. Apakah ini pertanyaan atau keluhan?
S.Lott
2
@ S.Lott, saya pikir Anda sedikit gelisah di sini. Saya menemukan saya jauh lebih sulit pada bahasa yang dikompilasi, dan sepertinya tidak mengganggu Anda.
zneak
@zneak: Pernyataan lain lebih dekat untuk menjadi faktual dan sulit diurai. Pernyataan itu paling mudah terbukti subyektif dan argumentatif.
S.Lott
1
@ S.Lott Apakah saya salah menyatakan bahwa Python menunjukkan satu kesalahan sekaligus?
zneak
1
@ S.Lott Maka semuanya pasti telah berubah, karena terakhir kali saya mencoba, kesalahan sintaksis apa pun akan menyebabkan Python berhenti mencoba "mengkompilasi" dan kesalahan nama akan melempar pengecualian dan tidak memeriksa sisa fungsi (meskipun ini memang meninggalkan ruang untuk melaporkan satu kesalahan per unit yang dapat diuji). Pernyataan subjektif dan argumentatif saya adalah pengantar untuk apa yang saya yakini sebagai fakta, tetapi jika itu tidak benar lagi saya akan pergi dan mengedit pertanyaan saya. Bagaimana cara kerjanya sekarang?
zneak

Jawaban:

6

Pertanyaan Anda tampaknya bukan tentang bagaimana kami melaporkan kesalahan kompiler - melainkan tentang klasifikasi masalah dan apa yang harus dilakukan tentang mereka.

Jika kita mulai dengan mengasumsikan, untuk saat ini, bahwa dikotomi peringatan / kesalahan sudah benar, mari kita lihat seberapa baik kita dapat membangun di atasnya. Beberapa ide:

  1. "Tingkat" peringatan yang berbeda. Banyak kompiler semacam ini menerapkan ini (misalnya GCC memiliki banyak switch untuk mengkonfigurasi persis apa yang akan memperingatkan tentang), tetapi perlu bekerja - misalnya, melaporkan keparahan apa peringatan yang dilaporkan, dan kemampuan untuk mengatur "peringatan" adalah kesalahan "untuk peringatan hanya di atas keparahan yang ditentukan.

  2. Klasifikasi kesalahan dan peringatan yang waras. Kesalahan hanya boleh dilaporkan jika kode tidak memenuhi spesifikasi, dan karenanya tidak dapat dikompilasi. Pernyataan yang tidak terjangkau, walaupun mungkin kesalahan pengkodean, harus berupa peringatan , bukan kesalahan - kode tersebut masih "valid", dan ada beberapa contoh yang sah di mana seseorang ingin dikompilasi dengan kode yang tidak dapat dijangkau (misalnya modifikasi cepat untuk debugging, misalnya) .

Sekarang hal-hal yang saya tidak setuju dengan Anda di:

  1. Berusaha ekstra untuk melaporkan setiap masalah. Jika ada kesalahan, itu merusak build. Bangunannya rusak. Build tidak akan berfungsi sampai kesalahan itu diperbaiki. Oleh karena itu, lebih baik melaporkan kesalahan itu segera, daripada "melanjutkan" untuk mencoba dan mengidentifikasi segala sesuatu yang lain "salah" dengan kode. Apalagi ketika banyak dari hal-hal itu mungkin disebabkan oleh kesalahan awal pula.

  2. Contoh spesifik Anda tentang peringatan-yang-seharusnya-menjadi-kesalahan. Ya, itu mungkin kesalahan programmer. Tidak, itu seharusnya tidak merusak bangunan. Jika saya tahu input ke fungsi sedemikian rupa sehingga akan selalu mengembalikan nilai, saya harus dapat menjalankan build dan melakukan beberapa tes tanpa harus menambahkan pemeriksaan tambahan tersebut. Ya, itu harus menjadi peringatan. Dan sangat parah sekali pada saat itu. Tapi itu seharusnya tidak merusak bangunan itu sendiri, kecuali kompilasi dengan peringatan-adalah-kesalahan.

Pikiran?

Segera.
sumber
Saya setuju dengan Anda, kecuali poin-poin di mana kami tidak setuju (ya), jadi itu +1 dari saya. Saya pikir itu cukup mudah untuk membuat setiap jalur kode baik mengembalikan nilai atau membatalkan program Anda, mengingat betapa buruknya ketika Anda benar-benar jatuh dalam kasus perilaku yang tidak terdefinisi.
zneak
7

Satu masalah yang Anda kemukakan adalah pelaporan kesalahan yang tidak lengkap - misalnya, melaporkan 2 kesalahan, dan ketika Anda memperbaikinya, Anda mendapat lebih banyak.

Ini (sebagian besar) kompromi dari penulis kompiler. Tergantung pada apa kesalahan yang telah Anda buat, itu sangat mudah untuk compiler untuk mulai salah paham apa yang Anda lakukan memiliki cukup parah sehingga mulai melaporkan kesalahan yang memiliki sangat sedikit hubungannya dengan realitas. Sebagai contoh, pertimbangkan salah ketik sederhana di mana Anda memiliki sesuatu seperti itn x;bukan int x;. Kecuali Anda telah melakukan sesuatu yang membuat itnsesuatu menjadi berarti, ini akan dilaporkan sebagai kesalahan. Sejauh ini tidak masalah, tetapi sekarang pertimbangkan apa yang terjadi selanjutnya - kompiler melihat banyak kode yang mencoba untuk digunakan x sebagai variabel. Haruskah A) berhenti dan membiarkan Anda memperbaikinya, atau B) memuntahkan 2000 kesalahan tentang error: "x": undeclared identifieratau sesuatu pada urutan itu? Pertimbangkan kemungkinan lain:

int main()[

Ini adalah salah ketik yang cukup jelas - jelas itu seharusnya {bukan [. Kompiler dapat memberi tahu Anda bagian itu dengan cukup mudah - tetapi haruskah kemudian melaporkan kesalahan untuk sesuatu seperti x=1;mengatakan sesuatu seperti error: statement only allowed inside a function?

Perhatikan bahwa ini bahkan masalah yang cukup sepele - yang jauh lebih buruk mudah ditemukan (terutama, seperti yang kita ketahui, ketika Anda masuk ke templat C ++). Intinya adalah bahwa penulis kompiler biasanya terjebak dengan mencoba kompromi antara melaporkan kesalahan palsu (yaitu, melaporkan sesuatu sebagai kesalahan, meskipun tidak masalah) dan gagal melaporkan kesalahan nyata. Ada beberapa aturan praktis yang harus diikuti untuk menjaga agar tidak melakukan kesalahan yang terlalu jauh di kedua arah, tetapi hampir tidak ada satupun yang mendekati sempurna.

Satu masalah lain yang Anda sebutkan adalah Java dan @SupressWarning. Ini sangat berbeda dari yang di atas - itu akan cukup sepele untuk diperbaiki. Satu-satunya alasan itu tidak diperbaiki adalah melakukan hal itu tidak sesuai dengan "karakter" dasar Jawa - yaitu, menurut mereka, "itu bukan bug, itu fitur." Meskipun itu biasanya hanya lelucon, dalam hal ini orang-orang yang terlibat begitu salah arah sehingga mereka benar-benar percaya itu benar.

Masalah yang Anda sebutkan dalam C dan C ++ dengan jalur kode yang tidak mengembalikan nilai tidak benar-benar memungkinkan untuk kompiler primitif. Itu untuk memungkinkan selama beberapa dekade kode yang ada , beberapa di antaranya tidak ada yang ingin memperbaiki, menyentuh, atau bahkan membaca. Itu kuno dan jelek tapi berhasil, dan tidak ada yang menginginkan apa pun selain itu untuk terus bekerja. Untuk lebih baik atau lebih buruk, komite bahasa yang cukup banyak terjebak dengan mempertahankan bahwa kompatibilitas, sehingga mereka terus membiarkan hal-hal yang tidak ada yang benar-benar menyukai - tetapi beberapa orang (setidaknya pikir mereka) kebutuhan.

Jerry Coffin
sumber
3
Selain poin Anda tentang kesalahan awal yang menyebabkan banyak kesalahan lainnya, ada juga fakta bahwa operan belakangan sering dibangun untuk meminta operan sebelumnya telah berhasil diselesaikan. Sebagai contoh, salah satu operan awal dalam kompiler C # memeriksa untuk memastikan bahwa tidak ada siklus dalam grafik warisan - Anda tidak memiliki warisan dari B yang mewarisi dari A. Jika Anda ingin melanjutkan dan menghasilkan daftar dari semua kesalahan setelah itu, setiap operan nanti harus dapat mengatasi siklus - membuatnya secara signifikan lebih lambat bahkan pada kompilasi "baik".
Anon.
@Segera. Kompiler Java membuat upaya yang lebih baik untuk bertahan melewati awal, dan saya tidak merasa secara signifikan lebih lambat. Bagi saya agak menjengkelkan betapa cepatnya cscmenyerah.
zneak
@ zneak: Seperti kata Jerry, ini adalah kompromi dari pihak pengembang kompiler. Menulis diagnosa kesalahan yang baik sebenarnya adalah masalah yang sangat sulit (lihat dentang untuk contoh seberapa jauh Anda benar-benar dapat mengambilnya). Lihat di sini untuk diskusi yang baik tentang fase dan pass kompiler C #.
Dean Harding