Comprobador de directrices básicas de C++ en Visual Studio 2017

Comprobador de directrices básicas de C++ en Visual Studio 2017

Esta publicación escrita por Sergiy Oryekhov y Andrew Pardoe

Las Directrices principales de C++ pueden ayudar a mejorar su código y disminuir su costo de mantenimiento al ofrecer una amplia gama de recomendaciones:alentar el uso de la biblioteca estándar, evitar el uso de prácticas inseguras siempre que sea posible, mantener un estilo consistente y ayudarlo a aplicar un diseño razonable. decisiones La cantidad de recomendaciones de las Directrices principales puede parecer desalentadora para aquellos que poseen código heredado, pero incluso un proceso de limpieza gradual brindará mejoras inmediatas a su código sin necesidad de una reescritura total.

Implementamos un conjunto de comprobaciones de análisis de código en Visual Studio que deberían ayudarlo a comenzar a avanzar hacia un código más seguro. Seguimos mejorando estas comprobaciones y agregando más comprobaciones específicas de las Directrices principales de C++, pero el conjunto actual le permite comenzar hoy mismo con el trabajo de mejorar el código existente y adoptar nuevos estándares de escritura de C++ moderno.

C++ Core Guidelines Checker no es nuevo:primero se lanzó como un paquete de NuGet y se incluye en Visual Studio 2017.  Se basa en las herramientas de análisis de código de C++ que se incluyen en todas las ediciones de Visual Studio.

En Visual Studio 2017 15.3, agregamos más comprobaciones y solucionamos varios errores. Proporcionaremos más detalles sobre estos controles en futuras publicaciones de blog. En esta publicación, veremos rápidamente cómo usar la herramienta y el tipo de problemas que puede detectar.

Ejecutar el verificador de pautas básicas de C++

El comprobador forma parte de las herramientas de análisis de código de C++. Supongamos que tenemos un proyecto nativo de C++. Para habilitar el análisis de código, podemos usar el IDE:

  • Seleccione el proyecto y en el menú contextual elija 'Propiedades'.
  • En el árbol "Propiedades de configuración", expanda el nodo "Análisis de código".
  • En la pestaña "General", marque "Habilitar análisis de código en compilación".

  • Cambie al nodo 'Extensiones' y habilite 'C++ Core Check (lanzado)'.

  • Guardar propiedades.
  • Ahora, cada compilación efectiva del proyecto (cuando hay algún cambio) debe ejecutar un análisis de código con comprobaciones extendidas.

Reglas de filtrado

Si está interesado en ver un subconjunto de reglas, puede usar conjuntos de reglas para administrar las advertencias que verá para un proyecto. Use la misma pestaña "General" (consulte "Cómo ejecutar C++ Core Check") para elegir un conjunto de reglas apropiado y luego reconstruir su proyecto:

También puede obtener un control más granular sobre las reglas mediante el uso de macros en combinación con la advertencia #pragma. Por ejemplo, aquí se explica cómo habilitar solo las reglas de seguridad de tipos:

#include <CppCoreCheck/Warnings.h>
#pragma warning(disable: ALL_CPPCORECHECK_WARNINGS)
#pragma warning(default: CPPCORECHECK_CONST_WARNINGS)

También puede habilitar reglas individuales para que sea más fácil de manejar cuando el código produce muchos resultados:

#pragma warning(default: WARNING_NO_REINTERPRET_CAST)

Problemas detectados por C++ Core Guidelines Checker

¿Qué tipo de problemas puede detectar C++ Core Guidelines Checker? Aquí hay una muestra de las reglas que están disponibles en el verificador. Los problemas detectados por estas reglas generalmente tienen alcance y se pueden solucionar sin una gran rotación de código. Puede concentrarse en un tipo de advertencias y resolverlas archivo por archivo.

Algunas de las siguientes correcciones hacen uso de una pequeña biblioteca de funciones, conocida como la biblioteca de soporte de pautas, diseñada para admitir las pautas básicas de C++.

  • Conversiones de tipo no seguras:
    // Don't use reinterpret_cast. It is hard to maintain safely.
    auto data = reinterpret_cast<char*>(buffer);
    

    La siguiente corrección se puede aplicar aquí:

    // To avoid buffer overruns use gsl::as_writeable_bytes which returns gsl::span.
    auto data = gsl::as_writeable_bytes<int>(gsl::make_span(buffer));
    
  • Gestión de recursos de bajo nivel no segura:
    // Avoid calling new and delete explicitly. Unique pointers are safer.
    auto buffer = new int[buffer_size];
    if (read_data(buffer, buffer_size) == read_status::insufficient_space)
        // Likely leaking memory here unless read_data deallocates it.
        buffer = new int[max_buffer_size];
    if (read_data(buffer, max_buffer_size) == read_status::insufficient_space)
    {
        delete[] buffer;
        return nullptr;
    }
    

    Para evitar problemas con la gestión de la memoria podemos reescribir este código:

    // std::unique_pointer will own and manage this object and dispose of it
    auto buffer = std::make_unique<int[]>(buffer_size);
    if (read_data(buffer.get(), buffer_size) == read_status::insufficient_space)
        buffer = std::make_unique<int[]>(max_buffer_size);
    if (read_data(buffer.get(), max_buffer_size) == read_status::insufficient_space)
        return nullptr;
    
  • Faltan especificaciones de constancia que pueden dar lugar a modificaciones de datos inesperadas con cambios de código posteriores:
    // If variable is assigned only once, mark it as const.
    auto buffer_size = count * sizeof(data_packet);
    auto actual_size = align(buffer_size);
    if (use_extension)
        actual_size += extension_size;
    encrypt_bytes(buffer, actual_size);
    

    La solución es trivial:

    // Now buffer_size is protected from unintentional modifications.
    const auto buffer_size = count * sizeof(data_packet);
    
  • C++ Core Guidelines Checker incluso puede detectar muchos problemas complejos, como este código al que le falta la limpieza de recursos en una de las rutas del código. En este ejemplo, el código usa un gsl::owner tipo de C++ Core Guidelines GSL.
    gsl::owner<int*> sequence = GetRandomSequence(); // This is not released.
    try
    {
        StartSimulation(sequence);
    }
    catch (const std::exception& e)
    {
        if (KnownException(e))
            return;                                  // Skipping resource cleanup here.
    
        ReportException(e);
    }
    delete [] sequence;
    

    En este caso GetRandomSequence() debe rediseñarse para devolver un puntero inteligente en lugar de gsl::owner para que se libere automáticamente cuando salga del alcance.

En cierre

Las buenas herramientas pueden ayudarlo a mantener y actualizar su código. Las Pautas básicas de C++ son un excelente lugar para comenzar, y el Verificador de pautas básicas de C++ puede ayudarlo a limpiar su código y mantenerlo limpio. ¡Pruebe el Comprobador de directrices básicas de C++ en Visual Studio 2017 y déjenos saber lo que piensa!

Si tiene algún comentario o sugerencia para nosotros, háganoslo saber. Puede comunicarse con nosotros a través de los comentarios a continuación, por correo electrónico ([email protected]) y puede enviar sus comentarios a través de Ayuda> Informar un problema en el producto o a través de la Comunidad de desarrolladores. También puede encontrarnos en Twitter (@VisualC) y Facebook (msftvisualcpp).