Sudah tiga bulan sejak 2018 berakhir. Bagi banyak orang, ini baru saja lewat, tetapi bagi kami, pengembang PVS-Studio, itu adalah tahun yang cukup penting. Kami berusaha keras, bersaing tanpa rasa takut untuk menyebarkan berita tentang analisis statis dan sedang mencari kesalahan dalam proyek sumber terbuka, ditulis dalam bahasa C, C ++, C #, dan Java. Dalam artikel ini, kami mengumpulkan 10 teratas yang paling menarik untuk Anda!
Untuk menemukan tempat paling menarik, kami menggunakan penganalisa kode statis
PVS-Studio . Itu dapat mendeteksi bug dan kerentanan potensial dalam kode, ditulis dalam bahasa yang tercantum di atas.
Jika Anda senang mencari kesalahan sendiri, Anda selalu dapat mengunduh dan mencoba penganalisa kami. Kami menyediakan
versi penganalisis gratis untuk siswa dan pengembang yang antusias,
lisensi gratis untuk pengembang proyek sumber terbuka, dan juga
versi uji coba untuk seluruh dunia dan anjingnya. Siapa tahu, mungkin pada tahun depan Anda akan dapat membuat 10 besar Anda sendiri? :)
Catatan: Saya mengundang Anda untuk memeriksa diri sendiri dan sebelum Anda melihat peringatan penganalisa, cobalah untuk mengungkapkan cacat Anda sendiri. Berapa banyak kesalahan yang dapat Anda temukan?
Tempat kesepuluh
Sumber:
Into Space Again: bagaimana Unicorn Mengunjungi StellariumKesalahan ini terdeteksi ketika memeriksa planetarium virtual yang disebut Stellarium.
Fragmen kode di atas, meskipun kecil, penuh dengan kesalahan yang cukup rumit:
Plane::Plane(Vec3f &v1, Vec3f &v2, Vec3f &v3) : distance(0.0f), sDistance(0.0f) { Plane(v1, v2, v3, SPolygon::CCW); }
Menemukannya?
Peringatan PVS-Studio :
V603 Objek telah dibuat tetapi tidak digunakan. Jika Anda ingin memanggil konstruktor, 'this-> Plane :: Plane (....)' harus digunakan. Plane.cpp 29
Pembuat kode bermaksud menginisialisasi bidang beberapa objek, menggunakan konstruktor lain, bersarang di yang utama. Yah, alih-alih itu, dia hanya berhasil membuat objek sementara hancur ketika meninggalkan ruang lingkupnya. Dengan demikian, beberapa bidang objek akan tetap tidak diinisialisasi.
Penulis seharusnya menggunakan konstruktor delegasi, yang diperkenalkan di C ++ 11, alih-alih panggilan konstruktor bersarang. Misalnya, dia bisa menulis seperti ini:
Plane::Plane(Vec3f& v1, Vec3f& v2, Vec3f& v3) : Plane(v1, v2, v3, SPolygon::CCW) { distance = 0.0f; sDistance = 0.0f; }
Dengan cara ini, semua bidang yang diperlukan akan diinisialisasi dengan benar. Luar biasa bukan?
Tempat kesembilan
Sumber:
Perl 5: Cara Menyembunyikan Kesalahan di MacroMakro yang sangat luar biasa menonjol dalam segala keindahannya di tempat kesembilan.
Ketika mengumpulkan kesalahan untuk menulis artikel, kolega saya Svyatoslav menemukan peringatan, yang dikeluarkan oleh penganalisa, yang terkait dengan penggunaan makro. Ini dia:
PP(pp_match) { .... MgBYTEPOS_set(mg, TARG, truebase, RXp_OFFS(prog)[0].end); .... }
Untuk mencari tahu apa yang salah, Svyatoslav menggali lebih dalam. Dia membuka definisi makro dan melihat bahwa itu berisi beberapa makro bersarang, beberapa di antaranya juga memiliki makro bersarang. Sangat sulit untuk masuk akal dari itu, jadi dia harus menggunakan file yang sudah diproses. Sayangnya, itu tidak membantu. Inilah yang ditemukan Svyatoslav di baris kode sebelumnya:
(((targ)->sv_flags & 0x00000400) && (!((targ)->sv_flags & 0x00200000) || S_sv_only_taint_gmagic(targ)) ? (mg)->mg_len = ((prog->offs)[0].end), (mg)->mg_flags |= 0x40 : ((mg)->mg_len = (((targ)->sv_flags & 0x20000000) && !__builtin_expect(((((PL_curcop)->cop_hints + 0) & 0x00000008) ? (_Bool)1 :(_Bool)0),(0))) ? (ssize_t)Perl_utf8_length( (U8 *)(truebase), (U8 *)(truebase)+((prog->offs)[0].end)) : (ssize_t)((prog->offs)[0].end), (mg)->mg_flags &= ~0x40));
Peringatan PVS-Studio :
V502 Mungkin
operator '?:' Bekerja dengan cara yang berbeda dari yang diharapkan. Operator '?:' Memiliki prioritas lebih rendah daripada operator '&&'. pp_hot.c 3036
Saya pikir, akan sulit untuk hanya melihat kesalahan seperti itu. Kami sudah lama berkutat pada kode ini, tetapi, terus terang, kami belum menemukan kesalahan di dalamnya. Bagaimanapun, ini adalah contoh yang lucu dari kode yang sulit dibaca.
Mereka mengatakan bahwa makro itu jahat. Tentu, ada beberapa kasus, ketika makro sangat diperlukan, tetapi jika Anda dapat mengganti makro dengan fungsi - Anda harus melakukannya.
Makro bersarang sangat penuh dengan jebakan. Bukan hanya karena sulit untuk memahami mereka, tetapi juga karena mereka dapat memberikan hasil yang tidak terduga. Jika seorang programmer membuat kesalahan dalam makro seperti itu - itu akan jauh lebih sulit untuk menemukannya dalam makro, daripada dalam suatu fungsi.
Tempat kedelapan
Sumber:
Chromium: Kesalahan LainnyaContoh berikutnya diambil dari serangkaian artikel tentang analisis proyek Chromium. Kesalahan bersembunyi di perpustakaan WebRTC.
std::vector<SdpVideoFormat> StereoDecoderFactory::GetSupportedFormats() const { std::vector<SdpVideoFormat> formats = ....; for (const auto& format : formats) { if (cricket::CodecNamesEq(....)) { .... formats.push_back(stereo_format); } } return formats; }
Peringatan PVS-Studio: V789 CWE-672 Iterators untuk wadah 'format', yang digunakan dalam rentang berbasis untuk loop, menjadi tidak valid pada panggilan fungsi 'push_back'. stereocodecfactory.cc 89
Kesalahannya adalah bahwa ukuran vektor
format bervariasi dalam rentang berbasis untuk loop. Lingkaran berbasis rentang didasarkan pada iterator, itu sebabnya perubahan ukuran kontainer di dalam loop tersebut dapat mengakibatkan pembatalan iterator ini.
Kesalahan ini tetap ada, jika menulis ulang loop dengan penggunaan iterator yang eksplisit. Untuk lebih jelasnya, saya bisa mengutip kode berikut:
for (auto format = begin(formats), __end = end(formats); format != __end; ++format) { if (cricket::CodecNamesEq(....)) { .... formats.push_back(stereo_format); } }
Misalnya, ketika menggunakan metode
push_back , realokasi vektor dapat terjadi - dengan cara ini, iterator akan mengatasi lokasi memori yang tidak valid.
Untuk menghindari kesalahan seperti itu, ikuti aturan: jangan pernah mengubah ukuran wadah di dalam lingkaran dengan kondisi terikat ke wadah ini. Ini juga berhubungan dengan loop berbasis rentang dan loop menggunakan iterator. Anda dapat membaca
diskusi ini tentang StackOverflow yang membahas topik operasi yang menyebabkan pembatalan iterator.
Tempat ketujuh
Sumber:
Godot: Tentang Penggunaan Analisis Statis Secara RegulerContoh pertama dari industri game adalah cuplikan kode yang kami temukan di mesin game Godot. Mungkin perlu beberapa usaha untuk menyadari kesalahannya, tetapi saya yakin bahwa pembaca yang mahir akan mengatasinya.
void AnimationNodeBlendSpace1D::add_blend_point( const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { p_at_index = blend_points_used; } else { for (int i = blend_points_used - 1; i > p_at_index; i++) { blend_points[i] = blend_points[i - 1]; } } .... }
Peringatan PVS-Studio: V621 CWE-835 Pertimbangkan untuk memeriksa operator 'untuk'. Ada kemungkinan bahwa loop akan dieksekusi secara tidak benar atau tidak akan dieksekusi sama sekali. animation_blend_space_1d.cpp 113
Mari kita lihat dari dekat kondisi loop. Variabel penghitung diinisialisasi oleh nilai
blend_points_used - 1 . Selain itu, dilihat dari dua pemeriksaan sebelumnya (dalam
ERR_FAIL_COND dan
jika ), menjadi jelas, bahwa pada saat eksekusi loop
blend_points_used ,
blend_points_used akan selalu lebih besar daripada
p_at_index . Dengan demikian, kondisi loop selalu benar atau loop tidak dijalankan sama sekali.
Jika
blend_points_used - 1 == p_at_index , loop tidak dieksekusi.
Dalam semua kasus lain, centang
i> p_at_index akan selalu benar, karena penghitung
i naik pada setiap iterasi loop.
Tampaknya loop itu abadi, tetapi tidak demikian.
Pertama, terjadi overflow integer dari variabel
i (yang merupakan perilaku tidak terdefinisi). Ini berarti, kita tidak harus bergantung padanya.
Jika
saya unsigned int , maka setelah penghitung mencapai nilai sebesar mungkin, operator
i ++ akan mengubahnya menjadi
0 . Perilaku seperti itu didefinisikan oleh standar dan disebut "Pembungkus tidak bertanda". Namun, Anda harus sadar bahwa penggunaan mekanisme semacam itu juga
bukan ide yang baik .
Itu adalah poin pertama, tetapi kita masih memiliki poin kedua! Kasusnya adalah kita bahkan tidak akan mendapatkan integer overflow. Indeks array akan keluar dari batas dengan cara sebelumnya. Ini berarti, bahwa akan ada upaya untuk mengakses memori di luar blok yang dialokasikan untuk array. Yang merupakan perilaku tidak terdefinisi juga. Contoh klasik :)
Saya dapat memberi Anda beberapa rekomendasi untuk mempermudah menghindari kesalahan serupa:
- Tulis kode yang sederhana dan mudah dimengerti
- Tinjau kode lebih menyeluruh dan tulis lebih banyak tes untuk kode yang baru ditulis
- Gunakan analisis statis;)
Tempat keenam
Sumber:
Amazon Lumberyard: A Scream of AnguishBerikut adalah contoh lain dari industri gamedev, yaitu dari kode sumber mesin AAA Amazon Lumberyard.
void TranslateVariableNameByOperandType(....) {
PVS-Studio warning :
V523 Pernyataan 'then' sama dengan pernyataan 'else'. toglsloperand.c 700
Amazon Lumberyard dikembangkan sebagai mesin lintas platform. Untuk alasan ini, pengembang mencoba mendukung sebanyak mungkin penyusun. Seperti yang dapat kita lihat dari komentar, seorang programmer Igor datang melawan kompiler Qualcomm.
Kita tidak tahu apakah dia berhasil melakukan tugasnya dan memeriksa pemeriksaan kompiler "paranoid", tetapi dia meninggalkan kode yang sangat aneh. Yang aneh tentang hal itu adalah bahwa kedua-
duanya - dan cabang
lain dari pernyataan
if berisi kode yang benar-benar identik. Kemungkinan besar, kesalahan seperti itu dihasilkan dari penggunaan metode Copy-Paste yang ceroboh.
Saya bahkan tidak tahu harus memberi saran apa di sini. Jadi saya hanya berharap semua pengembang Amazon Lumberyard memperbaiki kesalahan dan semoga sukses untuk pengembang Igor!
Tempat kelima
Sumber:
Sekali lagi, penganalisa PVS-Studio terbukti lebih perhatian daripada seseorangSebuah kisah menarik terjadi dengan contoh berikut ini. Rekan saya Andrey Karpov sedang mempersiapkan sebuah artikel tentang pemeriksaan lain dari kerangka kerja Qt. Ketika menulis beberapa kesalahan penting, dia menemukan peringatan penganalisa, yang dia anggap salah. Ini adalah fragmen kode dan peringatan untuk itu:
QWindowsCursor::CursorState QWindowsCursor::cursorState() { enum { cursorShowing = 0x1, cursorSuppressed = 0x2 }; CURSORINFO cursorInfo; cursorInfo.cbSize = sizeof(CURSORINFO); if (GetCursorInfo(&cursorInfo)) { if (cursorInfo.flags & CursorShowing)
Peringatan PVS-Studio: V616 CWE-480 'CursorShowing' bernama konstan dengan nilai 0 digunakan dalam operasi bitwise. qwindowscursor.cpp 669
Yang berarti, bahwa PVS-Studio mengeluh di tempat itu, yang jelas tidak memiliki kesalahan! Tidak mungkin untuk konstanta
CursorShowing menjadi
0 , karena hanya beberapa baris di atas yang diinisialisasi oleh
1 .
Karena Andrey menggunakan versi penganalisis yang tidak stabil, ia mempertanyakan kebenaran peringatan itu. Dia dengan hati-hati memeriksa potongan kode itu dan masih tidak menemukan bug. Dia akhirnya memberikannya positif palsu di bugtracker sehingga kolega lain bisa memperbaiki situasi.
Hanya analisis terperinci yang menunjukkan bahwa PVS-Studio ternyata lebih berhati-hati daripada seseorang lagi. Nilai
0x1 ditetapkan ke konstanta bernama bernama
cursorShowing sementara
CursorShowing mengambil bagian dalam operasi bitwise "dan". Ini adalah dua konstanta yang sama sekali berbeda, yang pertama dimulai dengan huruf kecil, yang kedua - dengan modal.
Kode berhasil dikompilasi, karena kelas
QWindowsCursor benar-benar berisi konstanta dengan nama ini. Inilah definisinya:
class QWindowsCursor : public QPlatformCursor { public: enum CursorState { CursorShowing, CursorHidden, CursorSuppressed }; .... }
Jika Anda tidak menetapkan nilai ke konstanta enum secara eksplisit, itu akan diinisialisasi secara default. Karena
CursorShowing adalah elemen pertama dalam enumerasi, maka akan diberikan
0 .
Untuk menghindari kesalahan seperti itu, Anda tidak harus memberikan entitas nama yang terlalu mirip. Anda harus mengikuti aturan ini dengan seksama jika entitas memiliki tipe yang sama atau dapat secara implisit dilemparkan satu sama lain. Seperti dalam kasus-kasus seperti itu hampir tidak mungkin untuk melihat kesalahan, tetapi kode yang salah masih akan dikompilasi dan tinggal di jalan yang mudah di dalam proyek Anda.
Tempat keempat
Sumber:
Tembak kaki Anda saat memegang data inputKami semakin dekat dengan tiga finalis teratas dan baris berikutnya adalah kesalahan dari proyek FreeSWITCH.
static const char *basic_gets(int *cnt) { .... int c = getchar(); if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf)-1] = '\0'; break; } .... }
Peringatan PVS-Studio: V1010 CWE-20 Data tercemar yang tidak dicentang digunakan dalam indeks: 'strlen (command_buf)'.
Penganalisis memperingatkan Anda bahwa beberapa data yang tidak dicentang digunakan dalam
strlen ekspresi
(command_buf) - 1 . Memang: jika
command_buf adalah string kosong dalam hal bahasa C (berisi satu-satunya karakter - '\ 0'),
strlen (command_buf) akan mengembalikan
0 . Dalam kasus seperti itu,
command_buf [-1] akan diakses, yang merupakan perilaku tidak terdefinisi. Itu buruk!
Semangat sebenarnya dari kesalahan ini bukan
mengapa itu terjadi, tetapi
bagaimana . Kesalahan ini adalah salah satu contoh terbaik, yang Anda "sentuh" ββsendiri, buat kembali. Anda dapat menjalankan FreeSwitch, melakukan beberapa tindakan yang akan mengarah pada pelaksanaan fragmen kode yang disebutkan di atas dan meneruskan string kosong ke input program.
Akibatnya, dengan gerakan tangan yang halus, sebuah program kerja berubah menjadi tidak bekerja! Anda dapat menemukan detail tentang cara mereproduksi kesalahan ini di artikel sumber melalui tautan yang diberikan di atas. Sementara itu, izinkan saya memberi Anda hasil yang memberi tahu:
Perlu diingat, bahwa data keluaran bisa berupa apa saja, jadi Anda harus selalu memeriksanya. Dengan cara ini, penganalisa tidak akan mengeluh dan program akan menjadi lebih andal.
Sekarang saatnya pergi untuk pemenang kami: kami berada di endgame sekarang! Ngomong-ngomong, finalis bug sudah menunggu lama, kemudian bosan dan bahkan mulai canggung. Lihat saja apa yang dipentaskan saat kami pergi!
Tempat ketiga
Sumber:
NCBI Genome Workbench: Riset Ilmiah di Bawah AncamanCuplikan kode dari proyek NCBI Genome Workbench, yang merupakan seperangkat alat untuk mempelajari dan menganalisis data genetik, membuka 3 pemenang teratas. Meskipun Anda tidak harus menjadi manusia super yang dimodifikasi secara genetik untuk menemukan bug ini, hanya sedikit orang yang tahu tentang kemungkinan untuk membuat kesalahan di sini.
void tds_answer_challenge(....) { .... if (ntlm_v == 1) { .... memset(hash, 0, sizeof(hash)); memset(passwd_buf, 0, sizeof(passwd_buf)); ... } else { .... } }
Peringatan PVS-Studio:- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'hash'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 365
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'passwd_buf'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 366
Apakah Anda menemukan bug? Jika ya, Anda seorang attaboy! .. atau manusia super yang dimodifikasi secara genetis.
Faktanya adalah bahwa kompiler pengoptimasi modern mampu melakukan banyak hal untuk memungkinkan program yang dibangun bekerja lebih cepat. Termasuk fakta bahwa kompiler sekarang dapat melacak bahwa buffer, diteruskan ke
memset , tidak digunakan di tempat lain.
Dalam hal ini, mereka dapat menghapus panggilan
memset yang "tidak perlu", memiliki semua hak untuk itu. Kemudian buffer yang menyimpan data penting dapat tetap tersimpan dalam memori untuk menyenangkan para penyerang.
Terhadap latar belakang ini, komentar geek ini "dengan keamanan adalah yang terbaik untuknya" terdengar lebih lucu. Dilihat oleh sejumlah kecil peringatan, diberikan untuk proyek ini, pengembangnya melakukan yang terbaik untuk menjadi tepat dan menulis kode aman. Namun, seperti yang bisa kita lihat, seseorang dapat dengan mudah mengabaikan cacat keamanan tersebut. Menurut Pencacahan Common Weakness, cacat ini diklasifikasikan sebagai
CWE-14 : Penghapusan Kompiler Kode ke Hapus Buffer.
Anda harus menggunakan fungsi
memset_s () agar
deallokasi memori aman. Fungsi ini lebih aman daripada
memset () dan tidak dapat diabaikan oleh kompiler.
Tempat kedua
Sumber:
Bagaimana PVS-Studio Terbukti Lebih Penuh Perhatian Daripada Tiga Setengah ProgramerSeorang peraih medali perak dengan ramah dikirim kepada kami oleh salah satu klien kami. Dia yakin bahwa penganalisa mengeluarkan beberapa positif palsu.
Evgeniy mendapat email, melihat melalui itu dan mengirim ke Svyatoslav. Svyatoslav melihat dari dekat potongan kode, yang dikirim oleh klien dan berpikir: "bagaimana mungkin analisa telah membuat kesalahan besar?". Jadi dia meminta nasihat pada Andrey. Dia juga memeriksa tempat itu dan memutuskan: memang, penganalisa menghasilkan positif palsu.
Begitulah, itu perlu diperbaiki. Hanya setelah Svyatoslav mulai membuat contoh-contoh sintetis untuk membuat tugas di pelacak bug kami, dia mendapatkan apa yang salah.
Tidak ada programmer yang dapat menemukan kesalahan, tetapi mereka benar-benar ada dalam kode. Terus terang, penulis artikel ini juga gagal menemukan mereka meskipun faktanya, penganalisa dengan jelas mengeluarkan peringatan untuk tempat yang salah!
Apakah Anda akan menemukan bug yang licik? Uji diri Anda pada kewaspadaan dan perhatian.
Peringatan PVS-Studio:- V560 Bagian dari ekspresi kondisional selalu salah: (ch> = 0x0FF21). decodew.cpp 525
- V560 Bagian dari ekspresi kondisional selalu benar: (ch <= 0x0FF3A). decodew.cpp 525
- V560 Bagian dari ekspresi kondisional selalu salah: (ch> = 0x0FF41). decodew.cpp 525
- V560 Bagian dari ekspresi kondisional selalu benar: (ch <= 0x0FF5A). decodew.cpp 525
Jika Anda melakukannya - pujian untuk Anda!
Kesalahannya terletak pada kenyataan bahwa operator negasi logis (!) Tidak diterapkan pada seluruh kondisi, tetapi hanya sub-ekspresi pertama:
!((ch >= 0x0FF10) && (ch <= 0x0FF19))
Jika kondisi ini benar, nilai variabel
ch terletak di kisaran [0x0FF10 ... 0x0FF19]. Dengan demikian, empat perbandingan lebih lanjut sudah tidak ada artinya: mereka akan selalu benar atau salah.
Untuk menghindari kesalahan seperti itu, ada baiknya tetap berpegang pada beberapa aturan. Pertama, sangat mudah dan informatif untuk meluruskan kode seperti tabel. Kedua, Anda tidak harus membebani ekspresi dengan tanda kurung. Misalnya, kode ini dapat ditulis ulang seperti ini:
const bool isLetterOrDigit = (ch >= 0x0FF10 && ch <= 0x0FF19)
Dengan cara ini, akan ada lebih sedikit tanda kurung dan di sisi lain - Anda akan lebih sering melihat kesalahan sesekali.
Di sinilah ceri di atas - mari kita beralih ke tempat pertama!
Tempat pertama
Sumber:
Sistem Terkejut: Kesalahan Menarik di Kode Sumber Shock Sistem LegendarisFinalis top hari ini adalah kesalahan dari System Shock yang legendaris! Ini adalah game yang dirilis cukup lama pada tahun 1994, yang menjadi pendahulu dan inspirasi untuk game ikonik seperti Dead Space, BioShock dan Deus Ex.
Tetapi pertama-tama saya harus mengakui sesuatu. Apa yang akan saya tunjukkan kepada Anda sekarang, tidak mengandung kesalahan. Sebenarnya, itu bahkan bukan sepotong kode, tapi aku tidak bisa membaginya denganmu!
Masalahnya adalah ketika menganalisis kode sumber permainan, kolega saya Victoria menemukan banyak komentar menarik. Dalam fragmen-fragmen yang berbeda, dia menemukan beberapa komentar cerdas dan ironis, dan bahkan puisi.
Beginilah komentar-komentar yang ditinggalkan dalam gim oleh pengembang di akhir tahun 90-an seperti ... Ngomong-ngomong, Doug Church - Kepala Perancang System Shock, juga sibuk menulis kode. Siapa tahu, mungkin beberapa komentar ini ditulis olehnya? Berharap, barang-barang pria di-handuk bukanlah hasil karyanya :)
Kesimpulan
Sebagai penutup, saya ingin mengucapkan terima kasih
kepada kolega saya karena mencari bug baru dan menulisnya di artikel. Terima kasih kawan! Tanpa Anda, artikel ini tidak akan semenarik itu.
Saya juga ingin menceritakan sedikit tentang pencapaian kami, karena sepanjang tahun kami tidak sibuk dengan hanya mencari kesalahan. Kami juga telah mengembangkan dan meningkatkan alat analisis, yang menghasilkan perubahan signifikan.
Sebagai contoh, kami telah menambahkan dukungan dari beberapa kompiler baru dan memperluas daftar aturan diagnostik. Kami juga telah mengimplementasikan dukungan awal standar
MISRA C dan MISRA C ++ . Fitur baru yang paling penting dan memakan waktu adalah dukungan bahasa baru. Ya, kami sekarang dapat menganalisis kode di
Jawa ! Dan terlebih lagi, kami memiliki
ikon yang diperbarui :)
Saya juga ingin mengucapkan terima kasih kepada pembaca kami. Terima kasih telah membaca artikel kami dan menulis kepada kami! Anda sangat responsif dan Anda sangat penting bagi kami!
10 kesalahan C ++ teratas kami di tahun 2018 telah berakhir. Fragmen apa yang paling Anda sukai dan mengapa? Apakah Anda menemukan beberapa contoh menarik di tahun 2018?
Semua yang terbaik, sampai jumpa lagi!