Static analysis of Salesforce code in PMD Apex / PMD Visualforce

I’ve been using PMD Apex / PMD Visualforce, a tool that allows static analysis of Apex and Visualforce code, a proprietary language used for Salesforce customization, and I’ll summarize the points of entry and CI innovations.

<! –more–>

PMD is the most widely known static analysis tool in the Java world, and it is also commonly used in Java [FindBugs] () to analyze compiled class files (*.class) instead of source code (*.java). FindBugs] (http://findbugs.sourceforge.net/), also commonly used in Java, analyzes not source code (*.java) but compiled class files (*.class) and is difficult to extend to other languages, but On the other hand, PMD has the advantage of being a toolkit that can be applied to various languages because it analyzes source code as an AST (Abstract Syntax Tree). When I was told by a colleague that PMD supports Apex/Visualforce, I was surprised to see that it was compatible with But now that I think about it, since Apex compiles and links on the Force.com server, PMD is a very good approach for client-side analysis.

As a side note, the “Don’t shoot the messenger**” in the PMD logo means “Don’t shoot the messenger**” in English-speaking countries, as in “Don’t get mad at the person who tells you bad news”. You know what I mean…:smile:

don’t shoot the messenger Meaning in the Cambridge English Dictionary

https://dictionary.cambridge.org/dictionary/english/don-t-shoot-the-messenger

Now, this article assumes that you have Apex/Visualforce source code at hand as a prerequisite. The following tools may be helpful: Salesforce DX is very engineer-friendly and is a great tool to adopt:sparkles:

So, let’s set up PMD. The nice thing is that the PMD distribution has both Apex and Visualforce right from the start. So, all you have to do is download PMD and run it with where the source code is, the expected output format, and the rule set you want to apply.

``bash cd $HOME wget https://github.com/pmd/pmd/releases/download/pmd_releases%2F5.8.1/pmd-bin-5.8.1.zip unzip pmd-bin-5.8.1.zip alias pmd="$HOME/pmd-bin-5.8.1/bin/bin/run.sh pmd” pmd -d /path/to/src -f html -R apex-apexunit,apex-braces,apex-complexity,apex-performance,apex-security,apex-style,vf-security


Note: `-d` is the directory above the various metadata directories:

- The `-d` option is OK if you specify the directory above the various metadata directories (i.e. `src`). To make it explicit, use the `-fileset` option instead of the `-d` option, and separate the pattern by commas <sup>[ref.1]</sup>.
- You can specify a language with `-l`, but in this example, the presence or absence of `-l` does not change the result
- `-f` is for specifying the output format, with the following choices <sup>[reference 1]</sup>:
  - `codeclimate`, `csv`, `emacs`, `html`, `ideaj`, `summaryhtml`, `text`, `textcolor`, `textpad`, `vbhtml`, `xml`, `xslt` (also for the `xsltFilename` property), `yahtml` (also for the `outputDir` property)

If you output an HTML report with `-f html`, you will get plain HTML like this** in the standard output**.

{

PMD HTML Report

When you use `-f vbhtml` to output an HTML report, you get this kind of HTML by default. It's nice that the output is organized by files. By the way, vb is like Vladimir Bossicard (the developer's name).

PMD HTML Report (VB)

`-f yahtml` is a mode called Yet Another HTML, which dumps multiple HTMLs into a directory, but unfortunately, Apex/Visualforce didn't generate any nice HTML. Note that you need to tell the location of the output directory with `-P outputDir=xxx`. Furthermore, the directory must already exist (usually just before `mkdir`). The `-f xml` is the format used by Jenkins plugins for aggregation and so on. If you're using [Visual Studio Code] (https://www.microsoft.com/ja-jp/dev/products/code-vs.aspx), there's a great plugin that automatically multiplies PMD Apex when you open or save it: > ChuckJonas/vscode-apex-pmd: PMD static analysis for Apex in vscode > > https://github.com/ChuckJonas/vscode-apex-pmd Well, I've written up to this point, but there was a serious problem in my environment. It seems that the Apex parsing part does not work properly in Java SE 9 (no problem with PMD Java and so on). It seems to be a reoccurrence of a bug that came out before, but the details are being analyzed. Let's get around it with Java SE 8 / OpenJDK 8 for now. The VSCode plugin mentioned above doesn't bark at all under Java 9 either:sweat_smile: ``Sweat_smile. Exception in thread "main" java.lang.IncompatibleClassChangeError: Inconsistent constant pool data in classfile for class apex/jorje/semantic/ symbol/member/method/Method/MethodTable. Method lambda$static$64(Lapex/jorje/semantic/symbol/member/method/Method/MethodInfo;)Z at index 85 is CONSTANT _MethodRef and should be CONSTANT_InterfaceMethodRef at apex.jorje.semantic.symbol.member.method.MethodTable.<clinit>(MethodTable.java:38) at apex.jorje.semantic.symbol.type.ModifierTypeInfo$Builder.build(ModifierTypeInfo.java:119) at apex.jorje.semantic.symbol.type.ModifierTypeInfos.<clinit>(ModifierTypeInfos.java:54) at apex.jorje.semantic.ast.modifier.ModifierGroups.<clinit>(ModifierGroups.java:27) at apex.jorje.semantic.symbol.type.TypeInfos.<clinit>(TypeInfos.java:40) at apex.jorje.semantic.symbol.member.variable.TriggerVariableMap.<clinit>(TriggerVariableMap.java:46) at apex.jorje.semantic.symbol.resolver.StandardSymbolResolver.<init>(StandardSymbolResolver.java:80) at apex.jorje.semantic.compiler.CompilerContext.<init>(CompilerContext.java:49) at apex.jorje.semantic.compiler.ApexCompiler.<init>(ApexCompiler.java:57) at apex.jorje.semantic.compiler.ApexCompiler.<init>(ApexCompiler.java:37) at apex.jorje.semantic.compiler.ApexCompiler$Builder.build(ApexCompiler.java:210) at net.sourceforge.pmd.lang.apex.ast.CompilerService.compile(CompilerService.java:95) at net.sourceforge.pmd.lang.apex.ast.CompilerService.visitAstsFromStrings(CompilerService.java:90) at net.sourceforge.pmd.lang.apex.ast.CompilerService.visitAstFromString(CompilerService.java:78) at net.sourceforge.pmd.lang.apex.ast.ApexParser.parseApex(ApexParser.java:42) at net.sourceforge.pmd.lang.apex.ast.ApexParser.parse(ApexParser.java:51) at net.sourceforge.pmd.lang.apex.ApexParser.parse(ApexParser.java:37) at net.sourceforge.pmd.SourceCodeProcessor.parse(SourceCodeProcessor.java:113) at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:175) at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:97) at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:52) at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:88) at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:27) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.base/java.lang.Thread.run(Thread.java:844)

Finally, I’ve been trying to figure out how to run it in GitLab CI.

Basically, all you have to do is hit the commands, but as I mentioned earlier, there are three things you need to do: watch out for the JRE version, don’t terminate even if the exit status is non-zero, and save the HTML report as artifacts.

The ``yml image: openjdk:8-jdk

STAGES:

  • pmd

pmd: stage: pmd Variables: PMD_VAR: ‘5.8.1’ PMD_OUT: ‘pmd.html’ RULESETS: ‘apex-apexunit,apex-braces,apex-complexity,apex-performance,apex-security,apex-style,vf-security’ script: - wget -q https://github.com/pmd/pmd/releases/download/pmd_releases%2F${PMD_VAR}/pmd-bin-${PMD_VAR}.zip - unzip -q pmd-bin-${PMD_VAR}.zip - echo `. /pmd-bin-${PMD_VAR}/bin/run.sh pmd -d src -f html -R ${RULESETS}`` > $PMD_OUT artifacts: paths: - $PMD_OUT


GitLab CI (as well as other scriptable CI services) uses a zero exit status to determine if the stage has succeeded or failed, and if it has failed, no further actions (including saving artifacts) are taken, so if you don't do anything about it, when PMD finds a problem (return 1 ), you won't be able to see the essential report. So, I used `echo` and backquotes to work around it. The `echo` results (standard output only) are redirected to a file to create a report.

Since the pmd stage is required to save the report when you set up build, test and deployment stages in Salesforce DX, etc., if you want to set up a failure in CI when you find a problem, it would be better to let the pmd stage pass and set the `pmd` stage to fail again (this time without any modifications) in the following pipeline.

PMD is currently being developed for the 6.0 major release. The ruleset categories will be cleaned up and the existing ruleset specifications may change (for the time being, they will remain for backward compatibility).

#### References.

1. [PMD &#x2013; Running PMD via command line](https://pmd.github.io/pmd-5.8.1/usage/running.html)
2. [PMD Apex &#x2013; PMD Rulesets index: current rulesets] (https://pmd.github.io/latest/pmd-apex/rules/index.html)
3. [PMD VF &#x2013; PMD Rulesets index: current rulesets] (https://pmd.github.io/latest/pmd-visualforce/rules/index.html)