Mengikuti jejak kalkulator: SpeedCrunch

Gambar 4

Studi tentang kode kalkulator berlanjut! Dalam ulasan ini, proyek SpeedCrunch akan ditinjau - yang paling populer kedua di antara kalkulator gratis.

Pendahuluan


SpeedCrunch adalah kalkulator ilmiah berbasis keyboard presisi dengan antarmuka pengguna yang cepat. Ini gratis, perangkat lunak sumber terbuka tersedia di Windows, Linux, dan macOS.

Kode sumber dihosting di BitBucket . Saya tidak terlalu suka dokumentasi perakitan, yang, menurut pendapat saya, harus ditulis lebih terinci. Persyaratan menentukan "Qt 5.2 atau yang lebih baru", meskipun beberapa paket spesifik diperlukan, yang tidak mudah dipelajari dari log CMake. Ngomong-ngomong, sekarang praktik yang baik untuk menerapkan Dockerfile ke proyek untuk dengan cepat mengkonfigurasi lingkungan pengembang yang diinginkan.

Untuk perbandingan dengan kalkulator lain, saya membawa output dari utilitas Cloc:

Gambar 2


Ulasan bug di proyek lain:


PVS-Studio digunakan sebagai alat analisis statis. Ini adalah serangkaian solusi untuk kontrol kualitas kode, mencari kesalahan dan kerentanan potensial. Bahasa yang didukung meliputi: C, C ++, C #, dan Java. Alat analisis dapat diluncurkan pada Windows, Linux dan macOS.

Logika aneh dalam satu lingkaran


V560 Bagian dari ekspresi kondisional selalu benar:! RuleFound. evaluator.cpp 1410

void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false; // <= // Rule for function last argument: id (arg) -> arg. if (!ruleFound && syntaxStack.itemCount() >= 4) { // <= Token par2 = syntaxStack.top(); Token arg = syntaxStack.top(1); Token par1 = syntaxStack.top(2); Token id = syntaxStack.top(3); if (par2.asOperator() == Token::AssociationEnd && arg.isOperand() && par1.asOperator() == Token::AssociationStart && id.isIdentifier()) { ruleFound = true; // <= syntaxStack.reduce(4, MAX_PRECEDENCE); m_codes.append(Opcode(Opcode::Function, argCount)); #ifdef EVALUATOR_DEBUG dbg << "\tRule for function last argument " << argCount << " \n"; #endif argCount = argStack.empty() ? 0 : argStack.pop(); } } .... } .... } 

Perhatikan variabel ruleFound . Pada setiap iterasi, disetel ke false. Tetapi jika Anda melihat tubuh seluruh siklus, maka dalam kondisi tertentu variabel ini disetel ke true, tetapi tidak akan diperhitungkan pada iterasi baru siklus. Kemungkinan besar, variabel ruleFound perlu dideklarasikan sebelum loop.

Perbandingan yang mencurigakan


V560 Bagian dari ekspresi kondisional selalu benar: m_scrollDirection! = 0. resultdisplay.cpp 242

 void ResultDisplay::fullContentScrollEvent() { QScrollBar* bar = verticalScrollBar(); int value = bar->value(); bool shouldStop = (m_scrollDirection == -1 && value <= 0) || (m_scrollDirection == 1 && value >= bar->maximum()); if (shouldStop && m_scrollDirection != 0) { // <= stopActiveScrollingAnimation(); return; } scrollLines(m_scrollDirection * 10); } 

Jika variabel shouldStop benar , maka variabel m_scrollDirection akan memiliki satu dari dua nilai: -1 atau 1. Oleh karena itu, dalam pernyataan kondisional berikut, nilai variabel m_scrollDirection pasti tidak akan nol, yang diperingatkan oleh penganalisa.

V668 Tidak ada gunanya menguji pointer 'item' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. editor.cpp 998

 void EditorCompletion::showCompletion(const QStringList& choices) { .... for (int i = 0; i < choices.count(); ++i) { QStringList pair = choices.at(i).split(':'); QTreeWidgetItem* item = new QTreeWidgetItem(m_popup, pair); if (item && m_editor->layoutDirection() == Qt::RightToLeft) item->setTextAlignment(0, Qt::AlignRight); .... } .... } 

Memori untuk objek bertipe QTreeWidgetItem dialokasikan menggunakan operator baru. Ini berarti bahwa jika alokasi memori dinamis tidak memungkinkan, pengecualian std :: bad_alloc () akan dibuang. Oleh karena itu, memeriksa penunjuk item tidak perlu dan dapat dihapus.

Potensi NULL Dereference


V595 Pointer 'ioparams' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 969, 983. floatio.c 969

 int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase; // <= .... } dot = '.'; expbegin = "("; expend = ")"; if (ioparams != NULL) // <= { dot = ioparams->dot; expbegin = ioparams->expbegin; expend = ioparams->expend; } .... } 

Pointer ioparams ditereferensi sebelum diperiksa validitasnya. Kemungkinan besar, kesalahan potensial merayap ke dalam kode. Karena dereferencing berada di bawah beberapa kondisi, masalah dapat memanifestasikan dirinya jarang, tetapi akurat.

Pembagian dengan nol


V609 Bagilah dengan nol. Kisaran penyebut [0..4]. floatconvert.c 266

 static int lgbase( signed char base) { switch(base) { case 2: return 1; case 8: return 3; case 16: return 4; } return 0; // <= } static void _setlongintdesc( p_ext_seq_desc n, t_longint* l, signed char base) { int lg; n->seq.base = base; lg = lgbase(base); // <= n->seq.digits = (_bitlength(l) + lg - 1) / lg; // <= n->seq.leadingSignDigits = 0; n->seq.trailing0 = _lastnonzerobit(l) / lg; // <= n->seq.param = l; n->getdigit = _getlongintdigit; } 

Fungsi lgbase memungkinkan nilai nol untuk dikembalikan, dimana divisi kemudian dilakukan. Berpotensi, apa pun selain nilai 2, 8, dan 16 dapat diteruskan ke fungsi.

Perilaku tidak terdefinisi


V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(~ 0)' negatif. floatlogic.c 64

 static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT; // <= if (sign < 0) longint->value[MAXIDX] |= mask; else longint->value[MAXIDX] &= ~mask; return sign; } 

Hasil inversi nol ditempatkan di int jenis tanda, sehingga hasilnya akan menjadi angka negatif, yang kemudian dilakukan pergeseran. Menggeser angka negatif ke kiri adalah perilaku yang tidak terdefinisi.

Seluruh daftar tempat berbahaya:

  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 289
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 325
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 344
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 351

Tag HTML Tidak Tertutup


V735 Mungkin HTML yang salah. Tag penutup "</body>" ditemukan, sedangkan tag "</div>" diharapkan. book.cpp 127

 static QString makeAlgebraLogBaseConversionPage() { return BEGIN INDEX_LINK TITLE(Book::tr("Logarithmic Base Conversion")) FORMULA(y = log(x) / log(a), log<sub>a</sub>x = log(x) / log(a)) END; } 

Seperti yang sering terjadi dengan kode C / C ++, tidak ada yang jelas dari sumbernya, jadi mari kita beralih ke kode preprocessed untuk fragmen ini:

Gambar 3



Analyzer mendeteksi tag div tidak tertutup. Ada banyak fragmen kode html dalam file ini dan sekarang harus diperiksa tambahan oleh pengembang.

Berikut adalah beberapa tempat mencurigakan yang ditemukan menggunakan PVS-Studio:

  • V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 344
  • V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 347

Operator penugasan


V794 Operator penugasan harus dilindungi dari kasus 'this == & other'. kuantitas.cpp 373

 Quantity& Quantity::operator=(const Quantity& other) { m_numericValue = other.m_numericValue; m_dimension = other.m_dimension; m_format = other.m_format; stripUnits(); if(other.hasUnit()) { m_unit = new CNumber(*other.m_unit); m_unitName = other.m_unitName; } cleanDimension(); return *this; } 

Disarankan untuk mempertimbangkan situasi ketika objek ditugaskan untuk dirinya sendiri dengan membandingkan pointer.

Dengan kata lain, dua baris kode berikut harus ditambahkan ke awal fungsi tubuh:

 if (this == &other) return *this; 

Pengingat


V601 Nilai 'salah' secara implisit dilemparkan ke tipe integer. cmath.cpp 318

 /** * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2. * Only valid for real numbers, since complex ones are not an ordered field. */ int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false; // FIXME: Return something better. } 

Terkadang dalam komentar untuk artikel kami, mereka menyarankan bahwa beberapa peringatan dikeluarkan pada kode yang tidak lengkap. Ya, itu terjadi, tetapi ketika itu benar-benar terjadi, itu langsung ditulis tentang hal itu.

Kesimpulan


Ulasan yang sudah tersedia dari tiga kalkulator: Windows Calculator, Qalculate! dan SpeedCrunch. Kami siap untuk terus meneliti kode kalkulator populer. Anda dapat menawarkan proyek untuk verifikasi, karena peringkat perangkat lunak tidak selalu mencerminkan gambaran nyata.

Periksa "Kalkulator" Anda dengan mengunduh PVS-Studio dan mencoba proyek Anda :-)



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Svyatoslav Razmyslov. Mengikuti Jejak Kalkulator: SpeedCrunch

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


All Articles