Berkontribusi untuk Pergi dengan pengurai go-critic


Anda mungkin ingat pengumuman terbaru dari penganalisa statis baru untuk Go yang disebut go-critic .


Saya memeriksa proyek golang / go dengannya dan mengirim beberapa tambalan yang memperbaiki beberapa masalah yang ditemukan di sana.


Dalam artikel ini, kami akan menganalisis kode yang diperbaiki, dan kami juga akan termotivasi untuk mengirim lebih banyak lagi perubahan seperti itu ke Go.


Untuk yang paling tidak sabar: daftar piala yang diperbarui .



dupSubExpr


Kita semua membuat kesalahan, dan, cukup sering, karena kurang perhatian. Go, menjadi bahasa di mana Anda kadang-kadang harus menulis kode membosankan dan boilerplate, kadang-kadang berkontribusi untuk kesalahan ketik dan / atau kesalahan salin / tempel.


CL122776 berisi perbaikan untuk bug yang ditemukan oleh dupSubExpr :


 func (a partsByVarOffset) Less(i, j int) bool { - return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[i]]) + return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]]) // ^__________________________________^ } 

Perhatikan indeks di kiri dan kanan. Sebelum koreksi, LHS dan RHS operator < identik, dan dupSubExpr bekerja untuk dupSubExpr .


commentedOutCode


Jika proyek Anda disponsori oleh sistem kontrol versi , maka alih-alih menonaktifkan kode dengan membungkusnya dalam komentar, sebaiknya hapus sepenuhnya. Ada pengecualian, tetapi lebih sering kode "mati" seperti itu mengganggu, membingungkan, dan dapat menyembunyikan kesalahan.


commentedOutCode dapat menemukan fragmen yang menarik ( CL122896 ):


 switch arch.Family { // ...  case clause. case sys.I386: return elf.R_386(nr).String() case sys.MIPS, sys.MIPS64: // return elf.R_MIPS(nr).String() // <- 1 case sys.PPC64: // return elf.R_PPC64(nr).String() // <- 2 case sys.S390X: // return elf.R_390(nr).String() // <- 3 default: panic("unreachable") } 

Ada komentar yang sedikit lebih tinggi:


 // We didn't have some relocation types at Go1.4. // Uncomment code when we include those in bootstrap code. 

Jika Anda beralih ke cabang go1.4 dan menghapus 3 baris ini dari komentar, kode tidak akan dikompilasi, namun jika Anda menghapus komentar pada panduan, semuanya akan berfungsi.


Biasanya, kode yang disembunyikan dalam komentar memerlukan penghapusan atau aktivasi balik.


Berguna, dari waktu ke waktu, untuk mengunjungi gema masa lalu seperti itu dalam kode Anda.


Tentang kesulitan deteksi

Ini adalah salah satu cek favorit saya, tetapi ini salah satu yang paling "berisik".


Banyak positif palsu untuk paket yang menggunakan math/big dan di dalam kompiler. Dalam kasus pertama, ini biasanya komentar penjelasan tentang operasi yang dilakukan, dan yang kedua, deskripsi kode yang menggambarkan fragmen AST. Membedakan komentar seperti itu dari kode "mati" nyata tanpa memperkenalkan negatif palsu adalah tidak biasa.


Ini memunculkan ide: bagaimana jika kita sepakat untuk entah bagaimana mengkhususkan kode di dalam komentar, yang jelas? Maka analisis statis akan disederhanakan. Ini bisa berupa hal sepele yang akan membuatnya mudah untuk mendefinisikan komentar penjelasan seperti itu, atau membuatnya menjadi kode Go yang tidak valid (misalnya, jika Anda menambahkan tanda pound, # ke awal baris).


Kategori lain adalah komentar dengan TODO eksplisit. Jika kode dihapus untuk komentar, tetapi ada deskripsi yang jelas tentang mengapa ini dilakukan dan ketika direncanakan untuk memperbaiki potongan kode ini, maka lebih baik tidak memberikan peringatan. Ini sudah diterapkan, tetapi bisa bekerja lebih andal.


boolExprSederhana


Terkadang orang menulis kode aneh. Mungkin menurut saya, tetapi ekspresi logis ( Boolean ) terkadang terlihat sangat aneh.


Go memiliki backend assembler x86 yang luar biasa (di sini air mata jatuh), tetapi ARM benar-benar salah:


 if !(o1 != 0) { break } 

"Jika tidak, o1 tidak sama dengan 0" ... Negasi ganda adalah klasik. Jika Anda menyukainya, saya mengundang Anda untuk membiasakan diri dengan CL123377 . Di sana Anda dapat melihat versi yang diperbaiki.


Opsi yang diperbaiki (bagi mereka yang tidak dapat dipancing untuk meninjau ulang)
 - if !(o1 != 0) { + if o1 == 0 { 

boolExprSimplify ditujukan untuk penyederhanaan yang meningkatkan keterbacaan (dan Go optimizer akan mengatasi masalah kinerja tanpanya).


underef


Jika Anda menggunakan Go dari versi sebelumnya, Anda dapat mengingat titik koma wajib, kurangnya referensi dereferensi otomatis, dan fitur lain yang saat ini hampir tidak mungkin dilihat dalam kode baru.


Di kode lama, Anda masih dapat melihat sesuatu seperti ini:


 // -    : buf := (*bufp).ptr() // ...     : buf := bufp.ptr() 

Beberapa pemicu underef tetap pada CL122895 .


appendCombine


Anda mungkin tahu bahwa append dapat mengambil beberapa argumen sebagai elemen untuk ditambahkan ke slice target. Dalam beberapa situasi, ini dapat sedikit meningkatkan keterbacaan kode, tetapi, yang mungkin lebih menarik, ini juga dapat mempercepat program Anda, karena kompiler tidak menekan panggilan append kompatibel ( cmd / compile: menggabungkan panggilan append ).


Di Go, cek appendCombine menemukan bagian berikut:


 - for i := len(ip) - 1; i >= 0; i-- { - v := ip[i] - buf = append(buf, hexDigit[v&0xF]) - buf = append(buf, '.') - buf = append(buf, hexDigit[v>>4]) - buf = append(buf, '.') - } + for i := len(ip) - 1; i >= 0; i-- { + v := ip[i] + buf = append(buf, hexDigit[v&0xF], + '.', + hexDigit[v>>4], + '.') + } 

 name old time/op new time/op delta ReverseAddress-8 4.10Β΅s Β± 3% 3.94Β΅s Β± 1% -3.81% (p=0.000 n=10+9) 

Detail dalam CL117615 .


rangeValCopy


Bukan rahasia lagi bahwa nilai-nilai yang diulang dalam range loop disalin. Untuk objek kecil, katakanlah, kurang dari 64 byte, Anda bahkan mungkin tidak memperhatikan ini. Namun, jika siklus seperti itu terletak pada jalur "panas", atau, yang Anda ulangi, mengandung sejumlah besar elemen, overhead dapat menjadi nyata.


Go memiliki tautan yang agak lambat (cmd / tautan), dan tanpa perubahan signifikan dalam arsitekturnya, perolehan kinerja yang kuat tidak dapat dicapai. Tetapi kemudian Anda dapat sedikit mengurangi inefisiensi dengan bantuan optimasi mikro. Setiap persen atau dua diperhitungkan.


Pemeriksaan rangeValCopy menemukan beberapa siklus dengan penyalinan data yang tidak diinginkan sekaligus. Inilah yang paling menarik dari mereka:


 - for _, r := range exports.R { - d.mark(r.Sym, nil) - } + for i := range exports.R { + d.mark(exports.R[i].Sym, nil) + } 

Alih-alih menyalin R[i] di setiap iterasi, kami hanya beralih ke satu-satunya anggota yang menarik bagi kami, Sym .


 name old time/op new time/op delta Linker-4 530ms Β± 2% 521ms Β± 3% -1.80% (p=0.000 n=17+20) 

Versi lengkap tambalan tersedia di: CL113636 .


bernamaConst


Di Go, sayangnya, konstanta bernama, bahkan dirangkai menjadi kelompok, tidak saling berhubungan dan tidak membentuk enumerasi ( proposal: spek: tambahkan dukungan enum yang diketik ).


Satu masalah adalah melemparkan konstanta yang tidak diketik ke tipe yang ingin Anda gunakan sebagai enum.


Misalkan Anda mendefinisikan jenis Color , ia memiliki nilai const ColDefault Color = 0 .
Manakah dari dua cuplikan kode ini yang lebih Anda sukai?


 // (A) if color == 0 { return colorBlack } // (B) if color == colorDefault { return colorBlack } 

Jika (B) tampaknya lebih sesuai untuk Anda, memeriksa namedConst akan membantu Anda melacak penggunaan nilai-nilai konstan bernama, melewati konstanta bernama itu sendiri.


Ini adalah bagaimana metode context.mangle dari paket html/template ditransformasikan:


  s := templateName + "$htmltemplate_" + c.state.String() - if c.delim != 0 { + if c.delim != delimNone { s += "_" + c.delim.String() } - if c.urlPart != 0 { + if c.urlPart != urlPartNone { s += "_" + c.urlPart.String() } - if c.jsCtx != 0 { + if c.jsCtx != jsCtxRegexp { s += "_" + c.jsCtx.String() } - if c.attr != 0 { + if c.attr != attrNone { s += "_" + c.attr.String() } - if c.element != 0 { + if c.element != elementNone { s += "_" + c.element.String() } return s 

Ngomong-ngomong, terkadang di tautan ke tambalan Anda dapat menemukan diskusi menarik ...
CL123376 adalah salah satu contohnya.


tidak baik


Ciri ekspresi slice adalah x[:] selalu identik dengan x jika tipe x adalah slice atau string. Dalam kasus irisan, ini berfungsi untuk semua jenis elemen []T .


Semua yang ada di daftar di bawah adalah sama ( x - slice):


  • x
  • x[:]
  • x[:][:]
  • ...

unslice menemukan ekspresi irisan redundan yang serupa. Ekspresi ini berbahaya, pertama-tama, dengan beban kognitif ekstra. x[:] memiliki semantik yang cukup signifikan dalam hal mengambil slice dari array. Irisan dengan rentang default tidak melakukan apa pun kecuali kebisingan.


Saya meminta patch di CL123375 .


switchTrue


Di CL123378 , " switch true {...} " diganti oleh " switch {...} ".
Kedua bentuk itu setara, tetapi yang kedua lebih idiomatis.
Ditemukan dengan memeriksa switchTrue .


Kebanyakan pemeriksaan gaya mengungkapkan kasus-kasus seperti itu di mana kedua opsi dapat diterima, tetapi salah satunya lebih umum dan akrab bagi lebih banyak programmer Go. Pemeriksaan selanjutnya dari seri yang sama.


ketikUnparen


Go, seperti banyak bahasa pemrograman lainnya, menyukai tanda kurung. Sedemikian rupa sehingga saya siap untuk menerima sejumlah dari mereka:


 type ( t0 int t1 (int) t2 ((int)) // ... ,  . ) 

Tetapi apa yang terjadi jika Anda menjalankan gofmt ?


 type ( t0 int t1 (int) // <- !   . t2 (int) // <-    ... ) 

Karena itulah typeUnparen ada. Dia menemukan dalam program semua jenis ekspresi di mana Anda dapat mengurangi jumlah tanda kurung. Saya mencoba mengirim CL123379 , mari kita lihat bagaimana komunitas akan menerimanya.


Getah tidak suka kawat gigi

Tidak seperti bahasa C-like, di Lisp tidak mudah untuk memasukkan tanda kurung yang tidak berguna di sembarang tempat. Jadi dalam bahasa yang sintaksisnya didasarkan pada ekspresi-S , menulis sebuah program yang tidak memiliki apa-apa selain memiliki sejumlah besar tanda kurung lebih sulit daripada dalam beberapa bahasa lain.


go-critic on go



Kami memeriksa hanya sebagian kecil dari cek yang dilaksanakan. Pada saat yang sama, kuantitas dan kualitas mereka hanya akan berkembang seiring waktu, termasuk terima kasih kepada orang-orang yang bergabung dalam pengembangan .


go-critic benar - benar gratis untuk penggunaan apa pun ( lisensi MIT ), dan juga terbuka untuk partisipasi Anda dalam pengembangan proyek. Kirimi kami gagasan untuk cek, Anda dapat segera mengimplementasikannya, melaporkan bug dan kekurangan yang ditemukan, membagikan kesan Anda. Anda juga dapat mengusulkan proyek untuk audit atau melaporkan ulasan kode Go Anda, pengalaman ini sangat berharga bagi kami.


Go Contributing Theme


Pernahkah Anda melihat artikel lokakarya kontribusi Go di Rusia ? Musim gugur ini akan menjadi babak kedua. Dan kali ini, selain format dan sponsor yang lebih sukses, kami akan memiliki senjata rahasia - penganalisa statis yang luar biasa. Akan ada kontribusi yang cukup sama sekali!


Tetapi pada kenyataannya, Anda dapat mulai sekarang (meskipun itu lebih baik - sedikit kemudian, setelah pembekuan kode ). Jika Anda berhasil merasa nyaman sebelum lokakarya berikutnya, itu akan sangat keren, karena kami sangat kekurangan mentor di Rusia.

Source: https://habr.com/ru/post/id416903/


All Articles