Code Style: checkstyle

code style checker

checkstyle (documentation here: https://checkstyle.sourceforge.io/) is a automated style checker that can be used on Java code.

When deployed on a Java project, it can be used to audit a code base for consistent style (e.g. indentation rules) when making commits and pull requests. CI/CD systems such as GitHub Actions can be used to automate running the checks.

Incorporating Checkstyle into a Java Maven Project

To try out checkstyle, first add this <plugin> to the <build> section of your pom.xml. Note that:

		<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-checkstyle-plugin</artifactId>
				<version>3.1.1</version>
				<configuration>
					<configLocation>sun_checks.xml</configLocation>
					<encoding>UTF-8</encoding>
					<consoleOutput>true</consoleOutput>
					<failsOnError>true</failsOnError>
					<linkXRef>false</linkXRef>
				</configuration>
				<executions>
					<execution>
						<id>validate</id>
						<phase>validate</phase>
						<goals>
							<goal>check</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

For example, the first time we ran this on the project-idea-reviewer repo, we got 777 checkstyle errors:

...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.158 s
[INFO] Finished at: 2020-05-19T09:30:00-07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.1.1:check (default-cli) on project demo-spring-google-oauth-app: Failed during checkstyle execution: There are 777 errors reported by Checkstyle 8.29 with sun_checks.xml ruleset. -> [Help 1]
...

Instead of tackling these all at once, perhaps a better strategy is to cut down the number of rules to a single rule, and then try to add back in rules one at a time. To do that, we’ll need to know how to deploy a custom style file instead of using one of the default ones built in to checkstyle.

A Strategy for deploying checkstyle

The value of <configLocation> (which is under <configuration>) indicates the location of an XML file that defines which rules are enforced by checkstyle. For example, this value indicates that the file sun_checks.xml is to be used.

<plugin>
  ...
  <configuration>
    <configLocation>sun_checks.xml</configLocation>
    ...
  <configuration>
  ...
<plugin>

The file sun_checks.xml is one that’s built into the checkstyle product, and is documented here: https://checkstyle.sourceforge.io/sun_style.html.

Another option is google_style.xml documented here: https://checkstyle.sourceforge.io/google_style.html. You can swap out sun_checks.xml for google_checks.xml and get a different report.

One major difference between the two is that while the sun_checks.xml file results in blocking errors, the google_checks.xml file results only in warnings.

Both sun_checks.xml and google_checks.xml can be specified without adding an additional file beyond adding the plugin to the pom.xml. However, you can also download a copy of either of these files and then tailor it to your own needs and preferences.

The originals of these files can be found in the GitHub repo for checkstyle here:

Copy one of these files into the main directory of your repo (or, if you prefer, under a checkstyle direcctory, e.g. checkstyle/checks.xml) and specify that file, e.g.

	<configLocation>checkstyle/checks.xml</configLocation>

Start with the total contents of one of the files, and then you can start editing down (or commenting out) the rules to a minimal, manageable, set. For example, here is a minimal version that started with google_checks.xml and made these changes:

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name = "Checker">
    <property name="charset" value="UTF-8"/>
    <property name="severity" value="error"/>
    <property name="fileExtensions" value="java, properties, xml"/>

    <!-- Checks for whitespace                               -->
    <!-- See http://checkstyle.org/config_whitespace.html -->
    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>

</module>

With this in place, we can run and see the number of violations:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.061 s
[INFO] Finished at: 2020-05-19T11:02:50-07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.1.1:check (default-cli) on project demo-spring-google-oauth-app: Failed during checkstyle execution: There are 227 errors reported by Checkstyle 8.29 with checkstyle/checks.xml ruleset. -> [Help 1]

Fixing Style Problems with Astyle

Fortunately, fixing these is straightforward using another tool called astyle, which is available on CSIL, and which can also be installed for Mac, Windows and Linux. (See the CS48 article on astyle for more info on installation options.)

In fact, astyle can be used to automatically fix quite a few style issues required by the Google code style for java(https://google.github.io/styleguide/javaguide.html). If you have astyle installed, you can simply run this command to fix all .java files under src.

astyle --style=google --recursive src/\*.java

Notes:

Note that the line <property name="fileExtensions" value="java, properties, xml"/> indicates that all .xml files and .properties files are also checked: however, astyle does not work on .properties and .xml files. So you will have to find other ways to get those files to comply with the Google checks, such as using the automatic formatting plugins built into editors such as VSCode.

After using astyle to make this transformation, running mvn checkstyle:check with the options above reports no violations:

% mvn checkstyle:check
...
[INFO] --- maven-checkstyle-plugin:3.1.1:check (default-cli) @ demo-spring-google-oauth-app ---
[INFO] Starting audit...
Audit done.
[INFO] You have 0 Checkstyle violations.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...
% 

The next step would be to add in additional rules, one by one, from the google_checks.xml, until you have reached full compliance with the Google spec.

Fixing problems manually

One option for fixing problems is to use a program such as astyle to make blanket changes.

But sometimes, there is a better way.

For example, consider this section of code which violated the Google style rule of no code past column 100:

                redirAttrs.addFlashAttribute("alertDanger", "Admin " + email + " was set from application properties and cannot be deleted.");

One way to fix this line is to try to put line breaks into it. But perhaps a better way is to factor out the message onto a separate line of code, using a variable:

                String message = "Admin " + email +
                                 " was set from application properties and cannot be deleted.";
                redirAttrs.addFlashAttribute("alertDanger", message);

You will need to decide on a strategy. Line length is tricky, because astyle uses max-code-length (which does not count white space indentation) while the Google Style guide (and checkstyle) works only with the total line length (including prefixed white space.) So if you are trying to enforce a line-length of 100, per the Google style guide, you may need to use something like:

astyle --max-code-length=90 --recursive src/\*.java

A value or 90 or 80 may reduce the number of violations to a level that you can then start inspecting by hand to see what the best strategy is.

Working with Google Style in IDEs

It can be challenging to maintain your code in good Google Style if your IDE is fighting you, i.e. if it’s own style formatting has a different conception of “what the rules are”.

You may want to look into whether you can find a plugin for your IDE that is consistent with the style rules you are enforcing in your project. If you use only rules that are a subset of the Google Style guide, then you can look for plugins that are Google Style guide compliant.

Making Exceptions for specific rule violations (by line of code)

Occasionally, you may have a good reason for violating one of the Google style rules.

You can indicate that specific lines of code in specific files should be exempted from specific rules by using the SupressionFilter module, as shown here:

   <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
   <module name="SuppressionFilter">
        <property name="file" value="checkstyle/suppressions.xml" />
    </module>

This specifies that if the file checkstyle/supressions.xml exists, it contains a list of the exceptions to the rules you are willing to permit.

As an example, suppose you got these errors when running mvn checkstyle:check:

[INFO] Starting audit...
[ERROR] /Users/pconrad/github/ucsb-cs48-s20/project-idea-reviewer/src/main/java/edu/ucsb/cs48/s20/demo/Application.java:9:1: Line contains a tab character. [FileTabCharacter]
[ERROR] /Users/pconrad/github/ucsb-cs48-s20/project-idea-reviewer/src/main/java/edu/ucsb/cs48/s20/demo/Application.java:10:1: Line contains a tab character. [FileTabCharacter]
[ERROR] /Users/pconrad/github/ucsb-cs48-s20/project-idea-reviewer/src/main/java/edu/ucsb/cs48/s20/demo/Application.java:11:1: Line contains a tab character. [FileTabCharacter]
[ERROR] /Users/pconrad/github/ucsb-cs48-s20/project-idea-reviewer/src/main/java/edu/ucsb/cs48/s20/demo/Application.java:12:1: Line contains a tab character. [FileTabCharacter]
[ERROR] /Users/pconrad/github/ucsb-cs48-s20/project-idea-reviewer/src/main/java/edu/ucsb/cs48/s20/demo/Application.java:22:1: Line contains a tab character. [FileTabCharacter]
Audit done

Suppose that for some reason, you really, really wanted those particular tab characters to stay just as they are, and not be reported as a style violation.

Here is an example of what you might put into checkstyle/supressions.xml to indicate this:

<?xml version="1.0"?>
 
<!DOCTYPE suppressions PUBLIC
     "-//Checkstyle//DTD SuppressionFilter Configuration 1.0//EN"
     "https://checkstyle.org/dtds/suppressions_1_0.dtd">
 
<suppressions>
  <suppress checks="FileTabCharacter"
             files="Application.java"
             lines="9-12,22"/>
</suppressions>

Making blanket exceptions for filename patterns

You can also specify exceptions to the rules by filename pattern.

For example, the sample google_check.xml includes these lines which use the [BeforeExecutionFileFilter](https://checkstyle.org/config_filefilters.html#BeforeExecutionExclusionFileFilter) to indicate that the rules should be ignored for any file with a name that ends in module-info.java

Note that the fileNamePattern parameter is a regular expression, hence the need for the \ characters in front of - and . and the need for the $ to indicate the end of the expression.

   <!-- Excludes all 'module-info.java' files              -->
   <!-- See https://checkstyle.org/config_filefilters.html -->
   <module name="BeforeExecutionExclusionFileFilter">
        <property name="fileNamePattern" value="module\-info\.java$"/>
   </module>

Another options for putting code in Google Style

Another option for addressing code style issues is

Note that this option is not configurable:

Note: There is no configurability as to the formatter’s algorithm for formatting. This is a deliberate design decision to unify our code formatting on a single format.