Clang-Tidy, parte 1: Modernice su código fuente con C++11 / C++14 refactorización automática de su código fuente con potentes herramientas de código abierto

16.03.2017 Kevin Funk

 Facebook  Twitter  LinkedIn  Correo electrónico

Esta serie de blogs presentará la utilidad clang-tidy del proyecto Clang / LLVM y mostrará cómo usarla para refactorizar automáticamente el código fuente de C++ e integrarse con su sistema de compilación, así como cómo usar la herramienta en otras plataformas que no sean Unidades.

Motivación: La alegría de las bases de código heredadas

C++11 agregó una cantidad significativa de nuevas características del lenguaje C++ que hasta la fecha todavía no se utilizan en su totalidad. Las más visuales son, sin duda, auto, override, expresiones Lambda, for basadas en rangos, sintaxis de inicialización uniforme, lo que sea. Si bien C++11 ya tiene varios años de antigüedad, todavía hay muchas bases de código que no utilizan ninguna de las nuevas características del lenguaje, ya sea por política de la administración o por pura pereza para evitar el esfuerzo de adaptación del lado del desarrollador. Clang-Tidy del proyecto de infraestructura del compilador LLVM está aquí para al menos superar esto último, para permitir la refactorización automática de su código fuente para que use las nuevas características del lenguaje.

Usar anulación, ¿alguien?

Ahora, ¿qué sucede si ya mantiene una base de código bastante grande que aún se compila en modo C++03, pero desea utilizar C++11 en el futuro, con todas las funciones útiles que tiene? Es probable que tengas montones y montones de código similar a este:

struct Base { virtual void reimplementMe(int a) {}};struct Derived : public Base { virtual void reimplementMe(int a) {}};

Hasta ahora, por supuesto, siempre ha vuelto a implementar el método de clase base correcto, ¡porque ha probado ampliamente su código a través de pruebas unitarias! Por supuesto que lo hiciste. Por lo tanto, el código está bien, pero ahora le gustaría seguir adelante. Le gustaría agregar el especificador override a cada método re-implementado en su base de código, pero por supuesto sin contratar a un aprendiz que pase por el código línea por línea y los agregue manualmente.

Solo para enfatizar que agregar override de hecho sirve para un propósito, recientemente corregimos un error en Qt 3D en el que no sobrecargábamos el método de clase base correcto. Con el especificador añadido anteriormente, nos habríamos dado cuenta al instante, después de la recompilación.

Tomaremos el ejemplo de anulación faltante para explicar el uso básico de clang-tidy.

¡Clang-Tidy al rescate!

Clang-Tidy es una herramienta de linter C++ basada en clang que proporciona un ejecutable de shell llamado clang-tidy como punto de entrada principal. Es un marco extensible para diagnosticar errores de programación típicos o problemas de estilo, generalmente cualquier cosa que se pueda detectar durante el análisis estático del código. El beneficio real de la herramienta es que, además, permite refactorizar automáticamente el código fuente mediante la aplicación de correcciones que cada problema individual puede proporcionar. Está fuertemente basado en complementos y viene con un útil conjunto de complementos listos para usar, que vamos a discutir en el siguiente párrafo.

 Logotipo de LLVM home inicio de clang-tidy

Clang-Tidy es una herramienta desarrollada y mantenida por la comunidad de Clang/LLVM.

Setup

Cuando se ejecuta Linux, clang-tidy suele ser fácil de conseguir a través del gestor de paquetes de su distribución. En Ubuntu Linux, por ejemplo, instalarlo es tan fácil como ejecutar los siguientes comandos:

% sudo apt-get install clang-tidy

Discutiremos la instalación de la herramienta en otras plataformas que no sean Linux en una de las próximas publicaciones del blog.

Nota: Le recomendamos que instale siempre la última versión (en el momento de escribir este artículo, se recomienda la versión basada en Clang/LLVM 3.9), ya que el número de complementos/comprobadores disponibles varía mucho de una versión a otra y crece constantemente.

Introducción

Nota: En esta entrada de blog, se utilizó clang-tidy-3.9

Una invocación típica de la herramienta de línea de comandos se ve así:

% clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...

Ejecutándolo de esta manera, la herramienta imprimirá un montón de advertencias y notas (si corresponde), de la misma manera que Clang/GCC también proporciona diagnósticos.

Clang-Tidy es una útil herramienta de análisis estático por sí sola con un montón de diferentes comprobadores disponibles, sin embargo, este no es el enfoque de esta publicación de blog. Preferiríamos aprovechar las potentes capacidades de refactorización de la herramienta para modernizar nuestro código fuente.

Listar comprobadores disponibles

Ejecutar la herramienta sin ningún parámetro de línea de comandos específico ejecutará el conjunto predeterminado de comprobadores habilitado por la utilidad. Vamos a ver qué otras fichas tiene para ofrecer (pasando-checks= ‘ * ‘ para verlas todas), y específicamente grep para las que tienen modernize en sus nombres. Esos comprobadores abogan por el uso de construcciones de lenguaje moderno:

$ clang-tidy --list-checks -checks='*' | grep "modernize" modernize-avoid-bind modernize-deprecated-headers modernize-loop-convert modernize-make-shared modernize-make-unique modernize-pass-by-value modernize-raw-string-literal modernize-redundant-void-arg modernize-replace-auto-ptr modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals modernize-use-default modernize-use-emplace modernize-use-nullptr modernize-use-override modernize-use-using

Impresionante lista de opciones, ¿no? Clang-Tidy de hecho envía algunas fichas interesantes de la caja (a partir de Clang / LLVM 3.9), con la lista creciendo constantemente de un lanzamiento a otro.

Los nombres de las fichas se explican por sí solos (por ejemplo, modernize-use-auto incluirá el uso automático cuando corresponda), pero si desea explorar lo que significa cada una de ellas, consulte la lista de fichas en la página de inicio de clang-tidy:

Para mostrar cómo se está utilizando la herramienta, centrémonos en el comprobador modernize-use-override, ya que es el comprobador más aplicable y no controvertido.

Refactorización de un solo archivo

Nuestro ejemplo de anulación de nuevo:

struct Base { virtual void reimplementMe(int a) {}};struct Derived : public Base { virtual void reimplementMe(int a) {}};

Ejecución de clang-tidy en el ejemplo (esta vez con el comprobador modernize-use-override habilitado):

% clang-tidy-3.9 -checks='modernize-use-override' test.cpp -- -std=c++111 warning generated./home/kfunk/test.cpp:5:18: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' virtual void reimplementMe(int a) {} ^ override

Muy bien. Así que notó que Derived::reimplementMe(int) anula un método de clase base, pero carece del especificador override. Ahora podríamos agregar eso manualmente manually o simplemente dejar que la herramienta lo haga por nosotros pasando-fix!

Ejecutándolo en el ejemplo (con modernize-use-override checker & fix-its habilitado):

% clang-tidy-3.9 -checks='modernize-use-override' -fix test.cpp -- -std=c++111 warning generated./home/kfunk/test.cpp:5:18: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' virtual void reimplementMe(int a) {} ^ override/home/kfunk/test.cpp:5:5: note: FIX-IT applied suggested code changes virtual void reimplementMe(int a) {} ^/home/kfunk/test.cpp:5:38: note: FIX-IT applied suggested code changes virtual void reimplementMe(int a) {} ^clang-tidy applied 2 of 2 suggested fixes.

Clang-tidy aplicó el fix-it e insertó override después de la declaración del método en la línea 5. Hecho!

Un par de notas

Hay algunas cosas que vale la pena mencionar:

  • No todas las fichas de clang-tidy en realidad llevan fix-its, pero las que comienzan con modernize todas lo hacen.
  • Puede usar fix-its de varios comprobadores al mismo tiempo (considere-checks=’modernize-use-override,modernize-use-auto’ -fix)
  • Ejecutar clang-tidy invoca la interfaz completa del compilador de Clang, por lo que necesitará algo de tiempo para completar
  • Los resultados de refactorización de clang-tidy son perfectamente precisos, debido al hecho de que está respaldado por un analizador de C++ completo

Refactorizar un proyecto completo (basado en CMake)

Hasta ahora hemos ejecutado clang-tidy en un solo archivo independiente. ¿Qué sucede si tiene una configuración de proyecto más compleja, con muchos archivos, todos con banderas de compilación personalizadas? Clang-tidy siempre funciona en un solo archivo, o mejor dicho, en una unidad de traducción. Podemos ayudar a la herramienta a determinar los indicadores de compilación correctos para cada unidad de traducción que compilamos en nuestro proyecto. La forma más conveniente de ejecutarlo es con una base de datos de comandos de compilación. CMake puede generar automáticamente uno, y una vez compile_commands.json está en su lugar y una versión de trabajo de clang-tidy está en la RUTA toda la base de código se puede analizar con run-clang-tidy.script py (normalmente enviado como parte de la instalación). Si no, simplemente puede descargarlo aquí.

Nota: Es muy recomendable utilizar run-clang-tidy.py para ejecutar clang-tidy en todo un proyecto, ya que ejecutará la herramienta varias veces en paralelo y se asegurará de que las ejecuciones simultáneas no interfieran entre sí (por ejemplo, evitando modificar un encabezado compartido en paralelo y, a su vez, generar código roto).

Generando un compile_commands.archivo json

Para generar los compile_commands.archivo json en un proyecto basado en CMake, simplemente ejecute:

% cd my-cmake-based-project% mkdir build% cd build% cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..

Use script para ejecutar clang-tidy

Ahora para ejecutar la herramienta con las comprobaciones predeterminadas en cada unidad de traducción del proyecto, simplemente invoque el script run-clang-tidy dentro del directorio con los comandos compile_commands.archivo json:

% run-clang-tidy.py

Como se ha visto antes, esto no modificará nada hasta ahora, ya que hemos ejecutado clang-tidy con solo las comprobaciones predeterminadas habilitadas. Para, por ejemplo, ejecutar la comprobación modernize-use-override en todas las unidades de traducción y refactorizar todo el código, se necesita esta invocación:

% run-clang-tidy.py -header-filter='.*' -checks='-*,modernize-use-override' -fix

Eso es. clang-tidy ahora se invocará en cada unidad de traducción de su proyecto y añadirá sobreescrituras cuando falten. El parámetro-header-filter=’.* ‘se asegura de que clang-tidy refaccione el código de los encabezados que se consumen en la unidad de traducción. El parámetro checks=’ -*,… ‘ se asegura de que todas las comprobaciones predeterminadas estén deshabilitadas.

Tenga en cuenta que las correcciones solo se aplican una vez que run-clang-tidy ha terminado. El script solo registrará los cambios a realizar y los aplicará todos a la vez al final.

Ejecutar otros comprobadores

De nuevo, modernize-use-override es solo un ejemplo, clang-tidy tiene muchos otros comprobadores que son útiles. Otro súper útil es el verificador modernize-use-nullptr, que transforma 0, o por ejemplo NULL literales en la versión moderna de C++11 nullptr. Para refactorizar todos los usos de los literales de estilo antiguo en su proyecto, simplemente ejecute:

% run-clang-tidy.py -header-filter='.*' -checks='-*,modernize-use-nullptr' -fix

Por lo general, es una buena idea realizar un verificador tras otro, confirmando resultados intermedios (piense en «Port towards C++11 nullptr», «Port towards C++11 override»,…) en su sistema de control de versiones.

Algunos ejemplos del mundo real

He utilizado personalmente clang-tidy en muchos proyectos diferentes, con resultados positivos. Recuerde, esta herramienta tiene un conocimiento perfecto de su código (ya que de hecho está utilizando la interfaz del compilador Clang) y, por lo tanto, refactorizará su código sin introducir código roto.

Ejemplos:

  • Este parche, por ejemplo, traslada todas las bibliotecas de Frameworks de KDE a C++11 nullptr, tocando alrededor de 9000 ubicaciones de código diferentes
  • Este parche traslada la base de código de KDE Marble a C++11 override, tocando alrededor de 2300 ubicaciones de código diferentes

Conclusión

Clang-Tidy es una herramienta poderosa que hace que transferir su base de código heredada a C++11 es cuestión de ejecutar un solo trazador. Viene con un gran conjunto de comprobadores predeterminados y la lista de los adicionales crece constantemente. Modernize-checkers se puede usar para modernizar/refactorizar su código fuente para usar nuevas características del lenguaje C++.

En la siguiente parte de esta serie discutiremos cómo usar clang-tidy en todo el proyecto con otros sistemas de compilación.

¿Necesita ayuda?

KDAB emplea a varios ingenieros que trabajan con herramientas Clang a diario. Estaremos encantados de ayudarle en caso de que tenga problemas con las herramientas de Clang en su proyecto o desee sacarle el máximo partido: permitiéndonos implementar comprobaciones de código específicas del proyecto o ejecutar herramientas de refactorización automáticas en su base de código. Hemos ayudado a los clientes a modernizar sus bases de código muy grandes con éxito utilizando herramientas, algo que no habría sido factible para ellos en cuanto a costos hacerlo manualmente.

póngase en Contacto con nosotros

FacebookTwitterLinkedInCorreo electrónico

Categorías: C++ / Seguridad Funcional / Cómo / KDAB Blogs / KDAB en Qt / Utillaje

Etiquetas: automatizado de refactorización / C++ / C++11 / C++14 / Clang / LLVM

Kevin Funk Ingeniero De Software Senior

Deja una respuesta

Tu dirección de correo electrónico no será publicada.