Celestia adalah simulator ruang tiga dimensi. Simulasi ruang memungkinkan penjelajahan alam semesta kita dalam tiga dimensi. Celestia tersedia di Windows, Linux dan macOS. Proyek ini sangat kecil dan PVS-Studio mendeteksi beberapa cacat di dalamnya. Terlepas dari kenyataan ini, kami ingin memperhatikannya, karena ini adalah proyek pendidikan yang populer dan akan berguna untuk meningkatkannya. Omong-omong, program ini digunakan dalam film populer, seri, dan program untuk menunjukkan ruang. Kenyataan ini, pada gilirannya, meningkatkan persyaratan terhadap kualitas kode.
Pendahuluan
Situs web resmi proyek
Celestia memberikan deskripsi terperinci. Kode sumber tersedia di
GitHub . Alat analisis memeriksa 166 file .cpp, tidak termasuk perpustakaan dan tes. Proyek ini kecil, tetapi cacat yang ditemukan patut diperhatikan.
Untuk melakukan analisis kode sumber kami menggunakan penganalisa kode statis
PVS-Studio . Baik Celestia dan PVS-Studio adalah cross-platform. Kami menganalisis proyek pada platform Windows. Sangat mudah untuk membangun proyek dengan mendapatkan dependensi menggunakan
Vcpkg - manajer perpustakaan Microsoft. Menurut ulasan, ini lebih rendah
dari kapasitas
Conan , tetapi program ini juga cukup nyaman untuk digunakan.
Hasil analisis
Peringatan 1V501 Ada sub-ekspresi yang identik ke kiri dan ke kanan operator '<': b.nAttributes <b.nAttributes cmodfix.cpp 378
bool operator<(const Mesh::VertexDescription& a, const Mesh::VertexDescription& b) { if (a.stride < b.stride) return true; if (b.stride < a.stride) return false; if (a.nAttributes < b.nAttributes)
Betapa mudahnya membuat kesalahan saat menyalin kode. Kami menulis tentang itu di setiap ulasan. Rupanya, hanya analisis kode statis yang dapat membantu dalam situasi ini.
Pemrogram menyalin ekspresi bersyarat dan tidak sepenuhnya mengeditnya. Versi yang benar kemungkinan besar sebagai berikut:
if (a.nAttributes < b.nAttributes) return true; if (b.nAttributes < a.nAttributes) return false;
Sebuah penelitian menarik tentang topik ini: "
Kejahatan dalam Fungsi Perbandingan ".
Peringatan 2V575 Fungsi 'memset' memproses elemen '0'. Periksa argumen ketiga. winmain.cpp 2235
static void BuildScriptsMenu(HMENU menuBar, const fs::path& scriptsDir) { .... MENUITEMINFO info; memset(&info, sizeof(info), 0); info.cbSize = sizeof(info); info.fMask = MIIM_SUBMENU; .... }
Penulis kode mencampur argumen kedua dan ketiga dari fungsi
memset . Alih-alih mengisi struktur dengan nol, ia mengatakan untuk mengisi 0 byte memori.
Peringatan 3V595 Pointer 'tujuan' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 48, 50. wintourguide.cpp 48
BOOL APIENTRY TourGuideProc(....) { .... const DestinationList* destinations = guide->appCore->getDestinations(); Destination* dest = (*destinations)[0]; guide->selectedDest = dest; if (hwnd != NULL && destinations != NULL) { .... } .... }
Pointer
tujuan mendapat dereferensi dua baris sebelum dibandingkan dengan
NULL . Kode semacam itu berpotensi menyebabkan kesalahan.
Peringatan 4Kelas
V702 harus selalu diturunkan dari std :: exception (dan yang sama) sebagai 'publik' (tidak ada kata kunci yang ditentukan, jadi kompiler secara default ke 'private'). fs.h 21
class filesystem_error : std::system_error { public: filesystem_error(std::error_code ec, const char* msg) : std::system_error(ec, msg) { } };
Penganalisis telah mendeteksi kelas yang diwarisi dari kelas
std :: exception melalui pengubah
pribadi (diatur secara default). Warisan seperti itu berbahaya karena
std :: exception exception tidak akan ditangkap karena pewarisan non-publik. Akibatnya, penangan pengecualian berperilaku tidak seperti yang dimaksudkan.
Peringatan 5V713 Pointer 's' digunakan dalam ekspresi logis sebelum diverifikasi terhadap nullptr dalam ekspresi logis yang sama. winmain.cpp 3031
static char* skipUntilQuote(char* s) { while (*s != '"' && s != '\0') s++; return s; }
Dalam satu bagian dari ekspresi kondisional, pemrogram lupa untuk mengubah pointer
s . Ternyata perbandingan pointer, bukan nilainya. Dan itu tidak masuk akal dalam situasi ini.
Peringatan 6V773 Fungsi itu keluar tanpa melepaskan pointer 'vertexShader'. Kebocoran memori dimungkinkan. modelviewwidget.cpp 1517
GLShaderProgram* ModelViewWidget::createShader(const ShaderKey& shaderKey) { .... auto* glShader = new GLShaderProgram(); auto* vertexShader = new GLVertexShader(); if (!vertexShader->compile(vertexShaderSource.toStdString())) { qWarning("Vertex shader error: %s", vertexShader->log().c_str()); std::cerr << vertexShaderSource.toStdString() << std::endl; delete glShader; return nullptr; } .... }
Memori dilepaskan oleh pointer
glShader tetapi tidak dihapus oleh pointer
vertexShader saat keluar dari fungsi.
Fragmen serupa di bawah ini:
- V773 Fungsi itu keluar tanpa melepaskan pointer 'fragmentShader'. Kebocoran memori dimungkinkan. modelviewwidget.cpp 1526
Peringatan 7Ekspresi
V547 '! InputFilename.empty ()' selalu benar. makexindex.cpp 128
int main(int argc, char* argv[]) { if (!parseCommandLine(argc, argv) || inputFilename.empty()) { Usage(); return 1; } istream* inputFile = &cin; if (!inputFilename.empty()) { inputFile = new ifstream(inputFilename, ios::in); if (!inputFile->good()) { cerr << "Error opening input file " << inputFilename << '\n'; return 1; } } .... }
Pemeriksaan berulang atas keberadaan nama file. Ini bukan bug, tetapi karena fakta bahwa variabel
inputFilename sudah diperiksa di awal fungsi, pemeriksaan di bawah ini dapat dihapus, membuat kode lebih kompak.
Peringatan 8V556 Nilai dari berbagai tipe enum dibandingkan: switch (ENUM_TYPE_A) {case ENUM_TYPE_B: ...}. render.cpp 7457
enum LabelAlignment { AlignCenter, AlignLeft, AlignRight }; enum LabelVerticalAlignment { VerticalAlignCenter, VerticalAlignBottom, VerticalAlignTop, }; struct Annotation { .... LabelVerticalAlignment valign : 3; .... }; void Renderer::renderAnnotations(....) { .... switch (annotations[i].valign) { case AlignCenter: vOffset = -font[fs]->getHeight() / 2; break; case VerticalAlignTop: vOffset = -font[fs]->getHeight(); break; case VerticalAlignBottom: vOffset = 0; break; } .... }
Nilai enumerasi digabungkan dalam operator sakelar. Karena itu, enumerasi tipe yang berbeda dibandingkan dalam satu fragmen:
LabelVerticalAlignment dan
AlignCenter .
Peringatan 9V581 Ekspresi kondisional dari pernyataan 'jika' yang terletak berdampingan adalah identik. Periksa baris: 2844, 2850. shadermanager.cpp 2850
GLVertexShader* ShaderManager::buildParticleVertexShader(const ShaderProperties& props) { .... if (props.texUsage & ShaderProperties::PointSprite) { source << "uniform float pointScale;\n"; source << "attribute float pointSize;\n"; } if (props.texUsage & ShaderProperties::PointSprite) { source << DeclareVarying("pointFade", Shader_Float); } .... }
Penganalisa telah mendeteksi dua ekspresi kondisional yang identik dalam satu baris. Entah kesalahan telah dibuat atau dua kondisi dapat digabungkan menjadi satu, dan dengan demikian membuat kode lebih sederhana.
Peringatan 10V668 Tidak ada gunanya menguji pointer 'dp' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. windatepicker.cpp 625
static LRESULT DatePickerCreate(HWND hwnd, CREATESTRUCT& cs) { DatePicker* dp = new DatePicker(hwnd, cs); if (dp == NULL) return -1; .... }
Nilai pointer yang dikembalikan oleh operator
baru dibandingkan dengan nol. Jika operator tidak dapat mengalokasikan memori, maka menurut standar C ++, pengecualian
std :: bad_alloc () dilemparkan. Maka cek untuk nol tidak ada gunanya.
Tiga cek serupa lainnya:
- V668 Tidak ada gunanya menguji pointer 'mode' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 2967
- V668 Tidak ada gunanya menguji pointer 'dropTarget' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3272
- V668 Tidak ada gunanya menguji pointer 'appCore' terhadap nol, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3352
Peringatan 11V624 Konstanta 3.14159265 sedang digunakan. Nilai yang dihasilkan mungkin tidak akurat. Pertimbangkan untuk menggunakan konstanta M_PI dari <math.h>. 3dstocmod.cpp 62
int main(int argc, char* argv[]) { .... Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); .... }
Diagnostik bersifat opsional tetapi dalam hal ini lebih baik menggunakan konstanta siap pakai untuk nomor Pi dari perpustakaan standar.
Kesimpulan
Baru-baru ini proyek ini telah dikembangkan oleh para penggemar, tetapi masih populer dan diminati dalam program pelatihan. Ada ribuan addons dengan objek ruang berbeda di Internet. Celestia digunakan dalam film "
The Day After Tomorrow " dan serial dokumenter "
Through the Wormhole with Morgan Freeman ".
Kami senang bahwa dengan memeriksa
berbagai proyek dengan kode sumber terbuka, kami tidak hanya mempromosikan metodologi analisis kode statis, tetapi juga berkontribusi pada pengembangan proyek sumber terbuka. Omong-omong, Anda juga dapat menggunakan penganalisa PVS-Studio tidak hanya untuk menguji proyek Anda sendiri, tetapi juga proyek pihak ketiga sebagai penggila. Untuk melakukan ini, Anda dapat menggunakan salah satu
opsi lisensi gratis.
Gunakan penganalisa kode statis, buat proyek Anda lebih andal dan lebih baik!