Análisis estático de código con Checkstyle

Hoy día la calidad de nuestro código no debería ser un factor negociable, y gracias a Checkstyle veremos que es muy fácil mantener el control sobre el cumplimiento de unos mínimos exigibles.

Índice de contenidos

Checkstyle Logo

1. Introducción

La calidad del código depende, entre otros factores, de los defectos que se introducen en el mismo durante las fases de desarrollo o mantenimiento.

Para mantener o aumentar la calidad de nuestro código debemos ayudarnos, entre otras herramientas, de técnicas de análisis estáticos de código que, básicamente, se encargan de buscar defectos en el código sin necesidad de que este se ejecute.

El catálogo de estas técnicas es amplio, algunos ejemplos pueden ser: Análisis de valores extremos, análisis del flujo de control y de datos, reglas de codificación, reglas de seguridad o análisis de variable viva, entre otros muchos.

Por suerte, actualmente existen múltiples herramientas que mantienen implementaciones de estas técnicas para evaluar de manera automática el código implementado en distintos lenguajes.

JavaScript:

  • JSLint
  • JSHint

Objective-C:

  • Clang

Java:

  • Checkstyle
  • FindBugs
  • PMD

En este tutorial vamos a centrarnos en Checkstyle, herramienta OpenSource distribuida bajo licencia GNU LGPL. Veremos cómo realizar análisis, cómo configurar su alcance y, finalmente, cómo extender sus comprobaciones mediante reglas propias.

¡Al lío!

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: macOS Sierra 10.12.3.
  • Entorno de desarrollo: IntelliJ IDEA ULTIMATE 2016.3
  • Apache Maven 3.3.0.
  • JDK 1.8.0_111
  • Checkstyle 7.1.2

3. Ejecución y Configuración de Checkstyle

Por lo general, la ejecución de checkstyle se realizará desde otra de las muchas herramientas para las que se mantienen plugins de integración, actualmente: Eclipse, IntelliJ, NetBeans, Maven o Gradle, SonarQube ó Jenkins, sin embargo, en este momento nos vamos a centrar en la ejecución manual del análisis.

Lo primero que debemos realizar para poder proceder con el análisis es descargar la última release de Checkstyle desde sourceForge ó de su versión en desarrollo desde github.

Para proceder con la ejecución basta con hacer uso del fichero JAR descargado como se muestra a continuación:

Con la opción -c establecemos el fichero de configuración que delimitará el alcance de nuestro análisis.

Por defecto, checkstyle nos ofrece 2 ficheros de configuración que podremos utilizar:

  • sun_checks.xml: Comprobará si nuestro código sigue un conjunto de estándares y buenas prácticas entre las que se incluyen las convenciones de Sun ó el JLS (Java Language Specification).
  • google_checks.xml: Comprobará si nuestro código sigue los estándares de Google para proyectos java (Pueden consultarse aquí).

Cualquiera de estas dos configuraciones nos servirá para conocer el estado de nuestro código a grandes rasgos y sin entrar en condiciones particulares.

En cualquier caso siempre podemos generar nuestros propios ficheros de configuración delimitando el alcance de los análisis, basta con seguir unas pautas como se puede ver aquí.

El resultado de la ejecución consistirá en un volcado por pantalla del total de las violaciones de las reglas que checkstyle haya detectado en su análisis, como se puede ver en el ejemplo a continuación:

4. Estructura y funcionamiento

Las funcionalidades incluidas en Checksyle se encuentran implementadas en módulos formando una estructura de árbol. Los módulos más cercanos a la raíz, el módulo “núcleo” de Checkstyle, implementan la interfaz FileSetCheck que, básicamente, toma un conjunto de ficheros de entrada y como resultado lanza mensajes de error.

Checkstyle ofrece una serie de implementaciones por defecto de FileSetCheck:

  • AbstractFileSetCheck.
  • AbstractHeaderCheck.
  • FileLengthCheck.
  • FileTabCharacterCheck.
  • HeaderCheck.
  • JavaPackageCheck.
  • RegexpHeaderCheck.

Nos vamos a centrar en la implementación TreeWalker, que ese encarga de transformar cada fichero de entrada Java en un árbol sintáctico (AST) y obtener el resultado de aplicar secuencialmente validaciones en forma de Checks (implementaciones de la clase abstracta AbstractCheck).

Es decir, TreeWalker se dedica a recorrer en profundidad (depth-first) el árbol AST, llamando a los Checks correspondientes según los siguientes pasos:

  1. Antes de que se ejecute ningún Check, TreeWalker permite realizar inicializaciones a través del método beginTree().
  2. A continuación y recursivamente irá descendiendo por cada nodo desde el nodo raíz hasta los nodos hojas, llamando al método visitToken() de cada Check para comprobar las validaciones correspondientes.
  3. Cuando un sub-árbol haya sido evaluado se lanzará el método leaveToken() dejando constancia de que la sub-estructura ya ha sido recorrida.
  4. Finalmente, cuando se vuelva al nodo raíz para finalizar el recorrido se lanzará el método finishTree().

A continuación veremos cómo, mediante la implementación de la clase abstracta AbstractCheck, podemos implementar el método visitToken donde implementaremos la lógica de nuestras reglas de validación.

5. Desarrollar nuestras propias reglas

Como en otras ocasiones, vamos a proceder con la generación de un proyecto basado en maven, donde vamos a definir las dependencias de nuestro proyecto. El fichero pom quedará como el que se puede ver a continuación:

A continuación, vamos a proceder con la implementación de una regla de ejemplo mediante TDD para lo que necesitamos poder probar el Check que vamos a desarrollar.

Para esto último vamos a hacer uso de 3 clases creadas por el equipo de desarrollo de Checkstyle y que se puede encontrar en su repositorio de github, en la sección de test, a saber: com.puppycrawl.tools.checkstyle.BaseCheckTestSupport, com.puppycrawl.tools.checkstyle.AuditEventUtFormatter y com.puppycrawl.tools.checkstyle.BriefUtLogger.

Haremos que nuestra clase de pruebas extienda la clase BaseCheckTestSupport y estableceremos los diferentes Tests. A continuación se muestra la clase de test.

Los ficheros RestcontrollerHandler.java y OtherClassToTest.java se encuentran en la ruta /test/resources/checks/myExampleCheck de nuestro proyecto.

La regla consistirá en comprobar que los métodos de nuestras clases no mantengan más de 15 caracteres. Esta regla en sí no tiene mucho sentido, pero nos servirá a modo de ejemplo para mostrar cómo proceder para implementar reglas.

A continuación, el código de nuestra regla:

A continuación vamos a explicar el código de la anterior clase:

  • El primer punto, como hemos visto anteriormente, pasa por extender la clase AbstractCheck.
  • Seguidamente, el método getDefaultTokens establece los tokens que identificarán los tipos de nodo sobre los que el método vistToken de este check en particular se ejecutará. Como la regla de validación que estamos creando comprueba la longitud del nombre de los métodos, estableceremos como único token a validar TokenTypes.METHOD_DEF. La clase TokenTypes nos ofrece la totalidad de los tipos de nodos existentes en el árbol AST.
  • Posteriormente establecemos un parámetro configurable que marcará la longitud máxima de los nombres de método.
  • Para finalizar implementamos el método visitToken donde estableceremos la lógica de validación. En su interior, la llamada al método log(methodNameNode.getLineNo(), message); da de alta una violación de la regla en el análisis global.
  • Tan solo queda establecer los parámetros de configuración a través del fichero de configuración xml:

    Tras empaquetar el proyecto podremos ejecutar el análisis incluyendo nuestra nueva regla mediante:

    El resultado será similar al que se puede ver a continuación:

    6. Conclusiones

    Como hemos visto, el análisis estático de código es una parte primordial en el mantenimiento y aseguramiento de la calidad de nuestro código y mediante Checkstyle podemos hacerlo de forma cómoda, rápida y sencilla.

    Además, podemos establecer el alcance de nuestros análisis y generar nuestras propias validaciones de calidad en caso de que nuestro contexto así lo requiera.

    ¡Ya no tenéis excusas para no proceder con estos análisis!

    7. Referencias