Celestia: las aventuras de los insectos en el espacio

Imagen 1

Celestia es un simulador espacial tridimensional. La simulación espacial nos permite explorar nuestro universo en tres dimensiones. Celestia está disponible en Windows, Linux y macOS. El proyecto es muy pequeño y en él, utilizando PVS-Studio, se detecta un número muy pequeño de defectos. Sin embargo, realmente queremos prestarle atención, ya que este es un proyecto educativo popular que es útil para mejorar. Por cierto, el programa se usa en películas, series y programas populares para representar el espacio. Lo que también aumenta los requisitos de calidad del código.

Introduccion


En el sitio web oficial del proyecto Celestia puede encontrar una descripción detallada del mismo. El código fuente del proyecto está alojado en GitHub . Excluyendo bibliotecas y pruebas, el analizador verificó 166 archivos .cpp. El proyecto es pequeño, pero los defectos encontrados son bastante interesantes.

Para analizar el código, utilizamos el analizador de código estático PVS-Studio . Tanto Celestia como PVS-Studio son multiplataforma. Para el análisis, se seleccionó la plataforma Windows. El proyecto fue fácil de construir al extraer dependencias usando Vcpkg , el administrador de la biblioteca de Microsoft. Según las revisiones, es inferior a Conan , pero este programa también fue conveniente de usar.

Resultados de analisis


Advertencia 1

V501 Hay subexpresiones idénticas a la izquierda y a la derecha del operador '<': 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) // <= return true; if (b.nAttributes < b.nAttributes) // <= return false; for (uint32_t i = 0; i < a.nAttributes; i++) { if (a.attributes[i] < b.attributes[i]) return true; else if (b.attributes[i] < a.attributes[i]) return false; } return false; } 

Qué fácil es cometer errores al copiar código. Escribimos sobre esto en cada revisión. Aparentemente, solo el análisis de código estático puede ayudar en esta situación.

El programador copió la expresión condicional y no la editó por completo. La opción correcta es muy probablemente esta:

 if (a.nAttributes < b.nAttributes) return true; if (b.nAttributes < a.nAttributes) return false; 

Un estudio interesante sobre este tema: "El mal vive en funciones de comparación ".

Advertencia 2

V575 La función 'memset' procesa elementos '0'. Inspeccione el tercer argumento. 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; .... } 

El autor del código mezcló el segundo y el tercer argumento con la función memset . En lugar de llenar la estructura con ceros, se indica que llene 0 bytes de memoria.

Advertencia 3

V595 El puntero de 'destinos' se utilizó antes de verificarlo con nullptr. Líneas de verificación: 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) { .... } .... } 

El puntero de destinos se desreferencia dos líneas más arriba que en comparación con NULL . Tal código podría conducir a un error.

Advertencia 4

Las clases V702 siempre deben derivarse de std :: exception (y similares) como 'público' (no se especificó ninguna palabra clave, por lo que el compilador lo predetermina a 'privado'). fs.h 21

 class filesystem_error : std::system_error { public: filesystem_error(std::error_code ec, const char* msg) : std::system_error(ec, msg) { } }; // filesystem_error 

El analizador detectó una clase heredada de la clase std :: exception a través del modificador privado (especificado por defecto). Esta herencia es peligrosa porque en caso de herencia no pública, cuando se intenta detectar la excepción std :: exception , se omitirá. Como resultado, los manejadores de excepciones no se comportan según lo previsto.

Advertencia 5

V713 El puntero 's' se utilizó en la expresión lógica antes de que se verificara contra nullptr en la misma expresión lógica. winmain.cpp 3031

 static char* skipUntilQuote(char* s) { while (*s != '"' && s != '\0') s++; return s; } 

En un lugar de la expresión condicional, olvidaron desreferenciar los punteros. El resultado fue una comparación del puntero, no el valor en él. Y esto no tiene sentido en esta situación.

Advertencia 6

V773 Se salió de la función sin soltar el puntero 'vertexShader'. Una pérdida de memoria es posible. 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; } .... } 

Cuando sale de la función, el puntero glShader libera la memoria , pero el puntero vertexShader no la borra .

El mismo lugar es más bajo en el código:

  • V773 Se salió de la función sin soltar el puntero 'fragmentShader'. Una pérdida de memoria es posible. modelviewwidget.cpp 1526

Advertencia 7

La expresión V547 '! InputFilename.empty ()' siempre es verdadera. 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; } } .... } 

Vuelva a verificar el nombre del archivo. Esto no es un error, pero debido al hecho de que la variable inputFilename ya está marcada al comienzo de la función, la verificación se puede eliminar a continuación, lo que hará que el código sea más compacto.

Advertencia 8

V556 Se comparan los valores de diferentes tipos de enumeración: 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; } .... } 

En la declaración de cambio , los valores de enumeración son confusos. Debido a esto, las enumeraciones de diferentes tipos se comparan en un solo lugar: LabelVerticalAlignment y AlignCenter .

Advertencia 9

V581 Las expresiones condicionales de las declaraciones 'if' ubicadas una al lado de la otra son idénticas. Líneas de verificación: 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); } .... } 

El analizador detectó dos expresiones condicionales idénticas en una fila. Hay un error o se pueden combinar dos condiciones en una sola, y así simplificar el código.

Advertencia 10

V668 No tiene sentido probar el puntero 'dp' contra nulo, ya que la memoria se asignó utilizando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. windatepicker.cpp 625

 static LRESULT DatePickerCreate(HWND hwnd, CREATESTRUCT& cs) { DatePicker* dp = new DatePicker(hwnd, cs); if (dp == NULL) return -1; .... } 

El valor del puntero devuelto por el nuevo operador se compara con cero. Si el operador no pudo asignar memoria, de acuerdo con el estándar del lenguaje C ++, se produce una excepción std :: bad_alloc () . Por lo tanto, verificar el puntero a igualdad a cero no tiene sentido.

Tres controles similares más:

  • V668 No tiene sentido probar el puntero de 'modos' contra nulo, ya que la memoria se asignó utilizando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. winmain.cpp 2967
  • V668 No tiene sentido probar el puntero 'dropTarget' contra nulo, ya que la memoria se asignó utilizando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. winmain.cpp 3272
  • V668 No tiene sentido probar el puntero 'appCore' contra nulo, ya que la memoria se asignó utilizando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. winmain.cpp 3352

Advertencia 11

V624 Se está utilizando la constante 3.14159265. El valor resultante podría ser inexacto. Considere usar la constante M_PI de <math.h>. 3dstocmod.cpp 62

 int main(int argc, char* argv[]) { .... Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); .... } 

Se recomienda el diagnóstico, pero es realmente mejor usar una constante lista para el número Pi de la biblioteca estándar.

Conclusión


Recientemente, el proyecto ha sido desarrollado por entusiastas, pero sigue siendo popular y muy solicitado en los programas de capacitación. En Internet hay miles de complementos con diferentes objetos espaciales. Celestia se usó en The Day After Tomorrow y la serie documental Through the Wormhole con Morgan Freeman .

Nos complace que al probar muchos proyectos de código abierto, no solo popularizamos la metodología de análisis de código estático, sino que también contribuimos al desarrollo del mundo de los proyectos abiertos. Por cierto, también puede usar el analizador PVS-Studio no solo para probar los suyos, sino también proyectos de terceros como entusiastas. Para hacer esto, puede usar una de las opciones de licencia gratuita.

Use analizadores de código estático, haga que sus proyectos sean más confiables y mejores.



Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace a la traducción: Svyatoslav Razmyslov. Celestia: Bugs 'Adventures in Space .

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


All Articles