Analyse statique de code Java : Google Error Prone, Findbugs et PMD

Illustration of a bug insect under a magnifying glass

Ce court article détaille comment mettre en place simplement 3 analyseurs de code statique avec Maven.

Contexte

Au sein de mon équipe à Voyages-Sncf, le nombre de composants Java est en train d'augmenter.

Nous avons déjà toute une batterie de tests pour valider notre code, ainsi qu'un Sonar en place.

Néanmoins, aucune analyse statique du code à la compilation, avant de commiter.

La mise en place de tels outils de validation a permis de détecter un bug majeur : Findbugs a repéré qu'un accesseur de DAO retournait le mauvais champ.

Pour l'occasion, j'ai fais un petit tour d'horizon des analyseurs de code Java utilisés aujourd'hui, et j'ai en testé 3.

Google Error Prone

Error Prone est un petit nouveau (2012) parmi les analyseurs de code statique. Il est implémenté comme un "hook" du compileur javac, et est très facile à mettre en place avec Maven :

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.3</version>
    <configuration>
        <compilerId>javac-with-errorprone</compilerId>
        <forceJavacCompilerUse>true</forceJavacCompilerUse>
        <source>8</source>
        <target>8</target>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-compiler-javac-errorprone</artifactId>
            <version>2.8.1</version>
        </dependency>
        <!-- override plexus-compiler-javac-errorprone's dependency on Error Prone with the latest version -->
        <dependency>
            <groupId>com.google.errorprone</groupId>
            <artifactId>error_prone_core</artifactId>
            <version>2.0.15</version>
        </dependency>
    </dependencies>
</plugin>

  En terme de config, on peut noter les tags source et target indiquant la version de Java utilisée.

Findbugs

Bien que le projet soit actuellement au point mort, Findbugs reste un outil très utilisé et très puissant. Tout un ensemble d'analyseurs supplémentaires peut même lui être ajouté via le plugin fb-contrib.

Voici comment le mettre en place avec Maven :

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>findbugs-maven-plugin</artifactId>
    <version>3.0.4</version>
    <configuration>
        <failOnError>true</failOnError>
        <threshold>Default</threshold>
        <effort>Max</effort>
        <omitVisitors>ExceptionSoftening,FieldCouldBeLocal,OverlyConcreteParameter,PossiblyRedundantMethodCalls,SuspiciousJDKVersionUse,UnnecessaryStoreBeforeReturn</omitVisitors>
        <plugins>
            <plugin>
                <groupId>com.mebigfatguy.fb-contrib</groupId>
                <artifactId>fb-contrib</artifactId>
                <version>6.8.1</version>
            </plugin>
        </plugins>
    </configuration>
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
   </executions>
</plugin>

Ici, l'exécution du plugin se fera en phase Maven compile. Si vous ne souhaitez pas utiliser fb-contrib, il suffit de supprimer la section plugins.

Le degré de sévérité des analyseurs, peut être contrôlé via les tags threshold et effort. Pour supprimer des erreurs, on peut éliminer entièrement un analyseur en l'incluant dans le champ omitVisitors, ou bien en utilisant l'annotation @SuppressFBWarning.

Enfin, cet analyseur peut également produire des rapports HTML (même si ça peut être un peu alambiqué à mettre en place), mais si on veut conserver le comportement failOnError il faut alors configurer deux phases d'exécution.

PMD

PMD est un outil très complet, capable d'analyser de nombreux languages. Encore une fois, il est assez simple à mettre en place avec Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>3.7</version>
    <configuration>
        <failOnViolation>true</failOnViolation>
        <failurePriority>5</failurePriority>
        <printFailingErrors>true</printFailingErrors>
        <linkXRef>false</linkXRef>
    </configuration>
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Encore une fois, l'exécution du plugin se fera en phase Maven compile.

printFailingErrors permet d'avoir une liste d'erreur en terminal.

Pour supprimer des erreurs, on peut utiliser une annotation ou bien un simple commentaire "NOPMD" en fin de ligne de code.

Infer

Facebook a développé un outil très prometteur nommé infer, mais malheureusement il ne tourne pas sous Windows.

Si malgré tout vous souhaitez le tester, il est possible de le lancer via Docker.

Comic strip: Oh no! The robotos are KILLING us!!! - Bu WHY?!? We never programmed them to do THIS!!! - <code snippet:> if (isCrazyMurderingRobot = true) kill(humans)