Initial commit for error-prone-contrib

- The `pom.xml` needs to be cleaned up a lot. Ideally we split off parts of the
  PRP parent pom so that it can be reused.
- Code style: maybe we should more closely follow the error-prone project by
  also using two-space indentation. This project could be an incubator for
  plugins contributed to EP. (It is for this reason also that I used Auto
  Service and JUnit.)
This commit is contained in:
Stephan Schroevers
2017-10-22 12:39:21 +02:00
commit 1f8fe5ff94
5 changed files with 1654 additions and 0 deletions

806
error-prone-contrib/pom.xml Normal file
View File

@@ -0,0 +1,806 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.picnicinternational.errorprone-contrib</groupId>
<artifactId>errorprone-contrib</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Picnic Error Prone Contrib</name>
<inceptionYear>2017</inceptionYear>
<organization>
<name>Picnic International</name>
<url>https://www.picnic.nl</url>
</organization>
<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<developerConnection>scm:git:git@github.com:PicnicSupermarket/error-prone-contrib.git</developerConnection>
<url>https://github.com/PicnicSupermarket/error-prone-contrib</url>
<tag>HEAD</tag>
</scm>
<issueManagement>
<system>Github</system>
<url>https://github.com/PicnicSupermarket/error-prone-contrib/issues</url>
</issueManagement>
<ciManagement>
<system>Travis CI</system>
<url>https://travis-ci.com/PicnicSupermarket/error-prone-contrib</url>
</ciManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.checkstyle>8.3</version.checkstyle>
<version.error-prone>2.1.2</version.error-prone>
<version.extra-enforcer-rules>1.0-beta-6</version.extra-enforcer-rules>
<version.javac>9-dev-r4023-3</version.javac>
<version.javadoc>2.10.4</version.javadoc>
<version.jdk>1.8</version.jdk>
<version.maven>3.5.0</version.maven>
<version.plexus-compiler>2.8.2</version.plexus-compiler>
<version.slf4j>1.7.25</version.slf4j>
<version.surefire>2.20.1</version.surefire>
<!-- Our build system (Travis CI) provides a monotonically increasing
build number. When building locally, this number is obviously absent.
So we provide a default value. -->
<build.number>LOCAL</build.number>
<!-- Arguments to the JVMs forked by Surefire. The specification of
this 'argLine' property instead of the definition of the 'argLine'
configuration setting allows users or plugins to specify additional
arguments. In particular, JaCoCo relies on this for the configuration
of its Java agent. -->
<argLine><!--
The test JVMs are short-running. By disabling certain expensive JIT
optimizations we actually speed up most tests. -->
-XX:TieredStopAtLevel=1
<!-- We cap memory usage. This is especially relevant on Travis CI,
but locally this should also be more than enough. -->
-Xmx512m
<!-- This argument cannot be set through Surefire's
'systemPropertyVariables' configuration setting. Setting the file
encoding is necessary because forked unit test invocations
otherwise use the environment's file encoding. -->
-Dfile.encoding=${project.build.sourceEncoding}
<!-- This argument *can* be set through Surefire's
'systemPropertyVariables' configuration setting, but by placing it
here it automatically also applies to the Failsafe plugin. On Unix
systems we use a lower-quality source of randomness, so as to avoid
potential slowdown of tests relying on java.security.SecureRandom.
Note that it is not fatal for the file to not exist, so this
setting is Windows-compatible. The '/./' syntax is no accident; see
https://bugs.openjdk.java.net/browse/JDK-6202721 for details. -->
-Djava.security.egd=file:/dev/./urandom
<!-- On Mac OS X, running in headless mode prevents the forked JVMs
from showing up in the dock and capturing window focus. -->
-Djava.awt.headless=true
<!-- When slf4j-test is used for the interception of log messages,
we also want them to be logged. -->
-Dslf4jtest.print.level=DEBUG
<!-- XXX: EXPLAIN -->
-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${version.javac}/javac-${version.javac}.jar
</argLine>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
<version>${version.error-prone}</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>${version.error-prone}</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_check_api</artifactId>
<version>${version.error-prone}</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<version>${version.error-prone}</version>
<exclusions>
<!-- We're using `com.google.errorprone:javac` instead. -->
<exclusion>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>javac</artifactId>
<version>${version.javac}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.2-jre</version>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>0.36</version>
</dependency>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${version.checkstyle}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>extra-enforcer-rules</artifactId>
<version>${version.extra-enforcer-rules}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${version.slf4j}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_check_api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_test_helpers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>javac</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<!-- Turn on filtering by default for application properties. -->
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application.properties</include>
<include>**/config.env</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<excludes>
<exclude>**/application.properties</exclude>
<exclude>**/config.env</exclude>
</excludes>
</resource>
<resource>
<directory>${basedir}/src/main/docker</directory>
<targetPath>${project.build.directory}</targetPath>
<filtering>true</filtering>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.github.PicnicSupermarket</groupId>
<artifactId>googleformatter-maven-plugin</artifactId>
<version>1.0.6-picnic-1-gjf-3b7ec93449</version>
<executions>
<execution>
<id>format-sources</id>
<phase>process-sources</phase>
<goals>
<goal>format</goal>
</goals>
<configuration>
<!-- We enable but skip the plugin by default.
This way `format.sh` can resolve its
dependencies in order to determine the version
of google-java-format used. Actual formatting
is done through explicit invocation by
`format.sh`. -->
<skip>true</skip>
</configuration>
</execution>
</executions>
<configuration>
<style>AOSP</style>
</configuration>
</plugin>
<plugin>
<groupId>com.lewisd</groupId>
<artifactId>lint-maven-plugin</artifactId>
<version>0.0.11</version>
<executions>
<execution>
<id>validate-pom</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<rules>
<excludes>
<exclude>OSS*</exclude>
</excludes>
</rules>
</configuration>
</plugin>
<plugin>
<groupId>de.thetaphi</groupId>
<artifactId>forbiddenapis</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>detect-forbidden-api-usage</id>
<goals>
<goal>check</goal>
<goal>testCheck</goal>
</goals>
</execution>
</executions>
<configuration>
<bundledSignatures>
<bundledSignature>jdk-system-out</bundledSignature>
</bundledSignatures>
<!-- The plugin tries to load all supertypes of any
class it analyzes. Some of those types may be absent
from the compilation classpath, because the module that
contains them has the `runtime` or `test` scope. When
this happens, we don't want to fail the build. (The
alternative is to declare all those dependencies
`provided`, but we'd rather not do that.) -->
<failOnMissingClasses>false</failOnMissingClasses>
<targetVersion>${version.jdk}</targetVersion>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<executions>
<execution>
<id>run-checkstyle</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<checkstyleRules>
<!-- We only enable rules that are not enforced by
error-prone or automatically corrected through
application of google-java-format. -->
<module name="Checker">
<module name="UniqueProperties" />
<module name="TreeWalker">
<module name="AnnotationUseStyleCheck">
<!-- XXX: Right now this check doesn't
completely enforce the desired style.
See https://github.com/checkstyle/checkstyle/issues/4972;
we're looking for the proposed `compact
= ALWAYS` and `singleArrayCurlies =
NEVER` style. -->
</module>
<module name="DeclarationOrder">
<!-- We don't enforce sorting fields by
their visibility modifier, for two
reasons:
- During (class) initialization
declaration order matters. Though the
plugin does not warn about fields
with obvious dependencies, its
dependency analysis is necessarily
incomplete; NPEs may result if some
of its advice is followed.
- Sometimes a field is annotated
`@VisibleForTesting`. It may then be
preferable not to reorder. The plugin
does not current respect this
annotation. -->
<property name="ignoreModifiers" value="true" />
</module>
<module name="DefaultComesLast" />
<module name="FinalClass" />
<module name="IllegalImport">
<!-- These packages are found in some
of the dependencies declared above. -->
<property name="illegalClasses" value="javax.xml.bind.DatatypeConverter" />
<property name="illegalClasses" value="org.springframework.context.annotation.ComponentScan" />
<property name="illegalPkgs" value="com.amazonaws.annotation" />
<property name="illegalPkgs" value="com.beust.jcommander.internal" />
<property name="illegalPkgs" value="com.newrelic.agent.deps" />
<property name="illegalPkgs" value="jdk" />
<property name="illegalPkgs" value="jersey.repackaged" />
<property name="illegalPkgs" value="nl.jqno.equalsverifier.internal" />
<property name="illegalPkgs" value="org.apache.commons.lang3">
<!-- Please use Guava or a custom
helper method instead. -->
</property>
<property name="illegalPkgs" value="org.mutabilitydetector.internal" />
</module>
<module name="IllegalImport">
<!-- XXX: This config uses
regexes so as to disallow static
imports. Once `illegalClasses`
disallows static imports by default,
this config can be merged into the one
above. See
https://github.com/checkstyle/checkstyle/issues/4954. -->
<property name="illegalClasses" value="org\.testng\.AssertJUnit(\..*?)?" />
<property name="regexp" value="true" />
</module>
<module name="MutableException" />
<module name="NeedBraces" />
<module name="NoClone" />
<module name="NoFinalizer" />
<module name="ParameterAssignmentCheck" />
<module name="RedundantModifier" />
<module name="SimplifyBooleanExpression" />
<module name="SimplifyBooleanReturn" />
<module name="WriteTag">
<property name="tag" value="@author" />
<property name="tagFormat" value="\S" />
<property name="tagSeverity" value="error" />
<property name="severity" value="ignore" />
</module>
<module name="UnnecessaryParentheses" />
<module name="UnusedImports">
<!-- Error-prone also detects these,
but (currently) doesn't warn about
JavaDoc-only imports. -->
</module>
</module>
</module>
</checkstyleRules>
<excludes>**/data/schema/**/*</excludes>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<!-- The plugin's "excludes" property accepts an Ant
pattern, but does not match against the full path. So
rather than explicitly excluding generated sources, we
have to explicitly include "original" sources. -->
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
<testSourceDirectories>${project.build.testSourceDirectory}</testSourceDirectories>
</configuration>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${version.checkstyle}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<compilerArgs>
<!-- XXX: Here, we could pass
-J-XX:TieredStopAtLevel=1, like we do for most
other JVMs started during the build. But that
doesn't seem to yield a noticable speedup. -->
</compilerArgs>
<maxmem>256m</maxmem>
<parameters>true</parameters>
<source>${version.jdk}</source>
<target>${version.jdk}</target>
<!-- Erroneously inverted logic... for details, see
https://jira.codehaus.org/browse/MCOMPILER-209 -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M1</version>
<executions>
<execution>
<id>apply-enforcement-rules</id>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
<configuration>
<rules>
<banDuplicatePomDependencyVersions />
<dependencyConvergence />
<requireJavaVersion>
<version>${version.jdk}</version>
</requireJavaVersion>
<requireMavenVersion>
<version>${version.maven}</version>
</requireMavenVersion>
<requirePluginVersions />
<requireSameVersionsReactor />
<requireUpperBoundDeps />
</rules>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>extra-enforcer-rules</artifactId>
<version>${version.extra-enforcer-rules}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>create-test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<skipIfEmpty>true</skipIfEmpty>
<archive>
<manifest>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
<Implementation-Version>${project.version}.${build.number}.${build.revision}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${version.javadoc}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.6</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>generate-source-jar</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.surefire}</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<!-- Timing measurements indicate that it's not
beneficial to perform method-level parallelization for
unit tests. -->
<parallel>classes</parallel>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<threadCount>2</threadCount>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>determine-build-number</id>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<buildNumberPropertyName>build.revision</buildNumberPropertyName>
<revisionOnScmFailure>UNKNOWN</revisionOnScmFailure>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tidy-maven-plugin</artifactId>
<version>1.0.0</version>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>modernize</id>
<phase>process-test-classes</phase>
<goals>
<goal>modernizer</goal>
</goals>
</execution>
</executions>
<configuration>
<javaVersion>${version.jdk}</javaVersion>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<!-- By default we verify various aspects of a module and the
artifact(s) it produces. We define these in checks in a profile
so that they can be disabled during development. -->
<id>verify-project</id>
<activation>
<property>
<name>!verification.skip</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.lewisd</groupId>
<artifactId>lint-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>de.thetaphi</groupId>
<artifactId>forbiddenapis</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs combine.children="append">
<arg>-Werror</arg>
<arg>-Xdoclint:reference</arg>
<arg>-Xlint:all</arg>
<!-- Some generated jOOQ code performs a
redundant cast. Until that is resolved we
suppress this warning. See
https://github.com/jOOQ/jOOQ/issues/6705. -->
<arg>-Xlint:-cast</arg>
<!-- Some of the dependencies we pull in
declare additional non-existent dependencies in
their MANIFEST.MF file. Since we're using Maven
this doesn't impact us. As such the resultant
compiler warnings are irrelevant. -->
<arg>-Xlint:-path</arg>
<!-- The Immutables.org annotation processor
does not handle all annotations present on the
classpath, and javac complains about this. That
doesn't make a lot of sense. From time to time
we should review whether this issue has been
resolved. -->
<arg>-Xlint:-processing</arg>
<!-- The JAXB-generated classes implement
java.io.Serializable, yet do not declare the
serialVersionUID field. Until that's solved, we
cannot enable "serial" warnings. -->
<arg>-Xlint:-serial</arg>
<!-- We want to enable almost all error-prone
bug pattern checkers, so we enable all and then
selectively deactivate some. -->
<arg>-XepAllDisabledChecksAsWarnings</arg>
<!-- The JAXB-generated classes exhibit some
error-prone bug patterns. Nothing we can do
about that, so we simply tell error-prone not
to warn about generated code. -->
<arg>-XepDisableWarningsInGeneratedCode</arg>
<!-- XXX: A whole bunch of JPA entities violate
this pattern. Revisit this once we phased out
Hibernate. See
https://picnic.atlassian.net/browse/PRP-6035. -->
<arg>-Xep:ConstructorInvokesOverridable:OFF</arg>
<!-- See https://github.com/google/error-prone/issues/655. -->
<arg>-Xep:ConstructorLeaksThis:OFF</arg>
<!-- See https://github.com/google/error-prone/issues/708. -->
<arg>-Xep:FieldMissingNullable:OFF</arg>
<!-- Disabled for now, but TBD. -->
<arg>-Xep:MethodCanBeStatic:OFF</arg>
<!-- See https://github.com/google/error-prone/issues/723. -->
<arg>-Xep:ReturnMissingNullable:OFF</arg>
<!-- Deals with an Android-specific limitation
not applicable to us. See also
https://github.com/google/error-prone/issues/488. -->
<arg>-Xep:StaticOrDefaultInterfaceMethod:OFF</arg>
<!-- Interesting idea, but way too much work for now. -->
<arg>-Xep:Var:OFF</arg>
<!-- The following bug patterns by default have
"SUGGESTION" severity. We raise them to the
"WARNING" level. See also
https://github.com/google/error-prone/issues/486. -->
<arg>-Xep:ConstantField:WARN</arg>
<arg>-Xep:EmptySetMultibindingContributions:WARN</arg>
<arg>-Xep:MixedArrayDimensions:WARN</arg>
<arg>-Xep:MultipleTopLevelClasses:WARN</arg>
<arg>-Xep:MultipleUnaryOperatorsInMethodCall:WARN</arg>
<arg>-Xep:MultiVariableDeclaration:WARN</arg>
<arg>-Xep:PackageLocation:WARN</arg>
<arg>-Xep:ParameterComment:WARN</arg>
<arg>-Xep:ParameterNotNullable:WARN</arg>
<arg>-Xep:PrivateConstructorForNoninstantiableModuleTest:WARN</arg>
<arg>-Xep:RemoveUnusedImports:WARN</arg>
<arg>-Xep:ThrowsUncheckedException:WARN</arg>
<arg>-Xep:UngroupedOverloads:WARN</arg>
<arg>-Xep:UnnecessarySetDefault:WARN</arg>
<arg>-Xep:UnnecessaryStaticImport:WARN</arg>
<arg>-Xep:UseBinds:WARN</arg>
<arg>-Xep:WildcardImport:WARN</arg>
</compilerArgs>
<compilerId>javac-with-errorprone</compilerId>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<showWarnings>true</showWarnings>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>${version.error-prone}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac</artifactId>
<version>${version.plexus-compiler}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac-errorprone</artifactId>
<version>${version.plexus-compiler}</version>
<!-- XXX: Drop this exclusion once we get rid
of our error-prone fork. -->
<exclusions>
<exclusion>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>analyze-dependencies</id>
<phase>process-test-classes</phase>
<goals>
<goal>analyze-dep-mgt</goal>
<goal>analyze-duplicate</goal>
<goal>analyze-only</goal>
</goals>
</execution>
</executions>
<configuration>
<failBuild>true</failBuild>
<failOnWarning>true</failOnWarning>
<ignoreDirect>false</ignoreDirect>
<ignoreNonCompile>true</ignoreNonCompile>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,166 @@
package com.picnicinternational.errorprone.bugpatterns;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
import com.google.auto.service.AutoService;
import com.google.common.base.VerifyException;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.method.MethodMatchers.MethodClassMatcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.tree.JCTree.JCMemberReference;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more specific types.
// XXX: Change this checker's name?
@AutoService(BugChecker.class)
@BugPattern(
name = "PrimitiveComparison",
summary =
"Ensure invocations of `Comparator#comparing{,Double,Int,Long}` match the return type"
+ " of the provided function",
severity = SeverityLevel.WARNING,
tags = StandardTags.LIKELY_ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION
)
public class BoxingComparisonCheck extends BugChecker implements MethodInvocationTreeMatcher {
private static final Matcher<ExpressionTree> STATIC_MATCH = getStaticTargetMatcher();
private static final Matcher<ExpressionTree> INSTANCE_MATCH = getInstanceTargetMatcher();
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
boolean isStatic = STATIC_MATCH.matches(tree, state);
if (!isStatic && !INSTANCE_MATCH.matches(tree, state)) {
return Description.NO_MATCH;
}
Type potentiallyBoxedType = getPotentiallyBoxedReturnType(tree.getArguments().get(0));
if (potentiallyBoxedType == null) {
return Description.NO_MATCH;
}
String actualMethodName = ASTHelpers.getSymbol(tree).getSimpleName().toString();
String preferredMethodName = getPreferredMethod(state, potentiallyBoxedType, isStatic);
if (actualMethodName.equals(preferredMethodName)) {
return Description.NO_MATCH;
}
Description.Builder description = buildDescription(tree);
tryFix(description, tree, preferredMethodName);
return description.build();
}
private String getPreferredMethod(VisitorState state, Type cmpType, boolean isStatic) {
Types types = state.getTypes();
Symtab symtab = state.getSymtab();
if (types.isSubtype(cmpType, symtab.intType)) {
return isStatic ? "comparingInt" : "thenComparingInt";
}
if (types.isSubtype(cmpType, symtab.longType)) {
return isStatic ? "comparingLong" : "thenComparingLong";
}
if (types.isSubtype(cmpType, symtab.doubleType)) {
return isStatic ? "comparingDouble" : "thenComparingDouble";
}
return isStatic ? "comparing" : "thenComparing";
}
@CheckForNull
private Type getPotentiallyBoxedReturnType(ExpressionTree tree) {
switch (tree.getKind()) {
case LAMBDA_EXPRESSION:
/* Return the lambda expression's actual return type. */
return ASTHelpers.getType(((LambdaExpressionTree) tree).getBody());
case MEMBER_REFERENCE:
/* Return the method's declared return type. */
// XXX: Very fragile. Do better.
Type subType2 = ((JCMemberReference) tree).referentType;
return ((MethodType) subType2).getReturnType();
default:
/* This appears to be a genuine `{,ToInt,ToLong,ToDouble}Function`. */
return null;
}
}
private void tryFix(
Description.Builder description,
MethodInvocationTree tree,
String preferredMethodName) {
ExpressionTree expr = tree.getMethodSelect();
switch (expr.getKind()) {
case IDENTIFIER:
SuggestedFix.Builder fix = SuggestedFix.builder();
fix.addStaticImport(
java.util.Comparator.class.getName() + '.' + preferredMethodName);
fix.replace(expr, preferredMethodName);
description.addFix(fix.build());
return;
case MEMBER_SELECT:
MemberSelectTree ms = (MemberSelectTree) tree.getMethodSelect();
description.addFix(
SuggestedFix.replace(ms, ms.getExpression() + "." + preferredMethodName));
return;
default:
throw new VerifyException("Unexpected type of expression: " + expr.getKind());
}
}
private static Matcher<ExpressionTree> getStaticTargetMatcher() {
MethodClassMatcher clazz = staticMethod().onClass(Comparator.class.getName());
return anyMatch(
unaryMethod(clazz, "comparingInt", ToIntFunction.class),
unaryMethod(clazz, "comparingLong", ToLongFunction.class),
unaryMethod(clazz, "comparingDouble", ToDoubleFunction.class),
unaryMethod(clazz, "comparing", Function.class));
}
private static Matcher<ExpressionTree> getInstanceTargetMatcher() {
MethodClassMatcher instance = instanceMethod().onDescendantOf(Comparator.class.getName());
return anyMatch(
unaryMethod(instance, "thenComparingInt", ToIntFunction.class),
unaryMethod(instance, "thenComparingLong", ToLongFunction.class),
unaryMethod(instance, "thenComparingDouble", ToDoubleFunction.class),
unaryMethod(instance, "thenComparing", Function.class));
}
@SafeVarargs
@SuppressWarnings("varargs")
private static Matcher<ExpressionTree> anyMatch(Matcher<ExpressionTree>... matchers) {
return (ExpressionTree t, VisitorState s) ->
Stream.of(matchers).anyMatch(m -> m.matches(t, s));
}
private static Matcher<ExpressionTree> unaryMethod(
MethodClassMatcher classMatcher, String name, Class<?> paramType) {
return classMatcher.named(name).withParameters(paramType.getName());
}
}

View File

@@ -0,0 +1,33 @@
package com.picnicinternational.errorprone.bugpatterns;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.MethodTree;
// XXX: Disable until fixed.
//@AutoService(BugChecker.class)
@BugPattern(
name = "EmptyMethod",
summary = "Empty method can likely be deleted",
severity = SeverityLevel.WARNING,
tags = StandardTags.LIKELY_ERROR
)
public final class EmptyMethodCheck extends BugChecker implements MethodTreeMatcher {
// XXX: Restrict: only warn about:
// - static methods
// - methods that are provably not in an inheritance hierarchy.
// - public methods in a Mockito test class.
@Override
public Description matchMethod(MethodTree tree, VisitorState state) {
if (tree.getBody() == null || !tree.getBody().getStatements().isEmpty()) {
return Description.NO_MATCH;
}
return buildDescription(tree).build();
}
}

View File

@@ -0,0 +1,601 @@
package com.picnicinternational.errorprone.bugpatterns;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class BoxingComparisonCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(BoxingComparisonCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new BoxingComparisonCheck(), getClass());
// XXX: There are no tests for multiple replacements within the same expression:
// - Error Prone doesn't currently support this, it seems.
// - The `BugCheckerRefactoringTestHelper` throws an exception in this case.
// - During actual compilation only the first replacement is applied.
// XXX: Can we perhaps work-around this by describing the fixes in reverse order?
@Test
public void testReplacementWithPrimitiveVariants() throws IOException {
refactoringTestHelper
.addInputLines(
"in/Test.java",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = Comparator.comparing(o -> (byte) 0);",
" Comparator<Object> cCmp = Comparator.comparing(o -> (char) 0);",
" Comparator<Object> sCmp = Comparator.comparing(o -> (short) 0);",
" Comparator<Object> iCmp = Comparator.comparing(o -> 0);",
" Comparator<Object> lCmp = Comparator.comparing(o -> 0L);",
" Comparator<Object> fCmp = Comparator.comparing(o -> 0.0f);",
" Comparator<Object> dCmp = Comparator.comparing(o -> 0.0);",
"",
" {",
" bCmp.thenComparing(o -> (byte) 0);",
" cCmp.thenComparing(o -> (char) 0);",
" sCmp.thenComparing(o -> (short) 0);",
" iCmp.thenComparing(o -> 0);",
" lCmp.thenComparing(o -> 0L);",
" fCmp.thenComparing(o -> 0.0f);",
" dCmp.thenComparing(o -> 0.0);",
" }",
"}")
.addOutputLines(
"out/Test.java",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = Comparator.comparingInt(o -> (byte) 0);",
" Comparator<Object> cCmp = Comparator.comparingInt(o -> (char) 0);",
" Comparator<Object> sCmp = Comparator.comparingInt(o -> (short) 0);",
" Comparator<Object> iCmp = Comparator.comparingInt(o -> 0);",
" Comparator<Object> lCmp = Comparator.comparingLong(o -> 0L);",
" Comparator<Object> fCmp = Comparator.comparingDouble(o -> 0.0f);",
" Comparator<Object> dCmp = Comparator.comparingDouble(o -> 0.0);",
"",
" {",
" bCmp.thenComparingInt(o -> (byte) 0);",
" cCmp.thenComparingInt(o -> (char) 0);",
" sCmp.thenComparingInt(o -> (short) 0);",
" iCmp.thenComparingInt(o -> 0);",
" lCmp.thenComparingLong(o -> 0L);",
" fCmp.thenComparingDouble(o -> 0.0f);",
" dCmp.thenComparingDouble(o -> 0.0);",
" }",
"}")
.doTest();
}
@Test
public void testReplacementWithBoxedVariants() throws IOException {
refactoringTestHelper
.addInputLines(
"in/Test.java",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = Comparator.comparingInt(o -> Byte.valueOf((byte) 0));",
" Comparator<Object> cCmp = Comparator.comparingInt(o -> Character.valueOf((char) 0));",
" Comparator<Object> sCmp = Comparator.comparingInt(o -> Short.valueOf((short) 0));",
" Comparator<Object> iCmp = Comparator.comparingInt(o -> Integer.valueOf(0));",
" Comparator<Object> lCmp = Comparator.comparingLong(o -> Long.valueOf(0));",
" Comparator<Object> fCmp = Comparator.comparingDouble(o -> Float.valueOf(0));",
" Comparator<Object> dCmp = Comparator.comparingDouble(o -> Double.valueOf(0));",
"",
" {",
" bCmp.thenComparingInt(o -> Byte.valueOf((byte) 0));",
" cCmp.thenComparingInt(o -> Character.valueOf((char) 0));",
" sCmp.thenComparingInt(o -> Short.valueOf((short) 0));",
" iCmp.thenComparingInt(o -> Integer.valueOf(0));",
" lCmp.thenComparingLong(o -> Long.valueOf(0));",
" fCmp.thenComparingDouble(o -> Float.valueOf(0));",
" dCmp.thenComparingDouble(o -> Double.valueOf(0));",
" }",
"}")
.addOutputLines(
"out/Test.java",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = Comparator.comparing(o -> Byte.valueOf((byte) 0));",
" Comparator<Object> cCmp = Comparator.comparing(o -> Character.valueOf((char) 0));",
" Comparator<Object> sCmp = Comparator.comparing(o -> Short.valueOf((short) 0));",
" Comparator<Object> iCmp = Comparator.comparing(o -> Integer.valueOf(0));",
" Comparator<Object> lCmp = Comparator.comparing(o -> Long.valueOf(0));",
" Comparator<Object> fCmp = Comparator.comparing(o -> Float.valueOf(0));",
" Comparator<Object> dCmp = Comparator.comparing(o -> Double.valueOf(0));",
"",
" {",
" bCmp.thenComparing(o -> Byte.valueOf((byte) 0));",
" cCmp.thenComparing(o -> Character.valueOf((char) 0));",
" sCmp.thenComparing(o -> Short.valueOf((short) 0));",
" iCmp.thenComparing(o -> Integer.valueOf(0));",
" lCmp.thenComparing(o -> Long.valueOf(0));",
" fCmp.thenComparing(o -> Float.valueOf(0));",
" dCmp.thenComparing(o -> Double.valueOf(0));",
" }",
"}")
.doTest();
}
@Test
public void testReplacementWithPrimitiveVariantsUsingStaticImports() throws IOException {
refactoringTestHelper
.addInputLines(
"in/Test.java",
"import static java.util.Comparator.comparing;",
"",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = comparing(o -> (byte) 0);",
" Comparator<Object> cCmp = comparing(o -> (char) 0);",
" Comparator<Object> sCmp = comparing(o -> (short) 0);",
" Comparator<Object> iCmp = comparing(o -> 0);",
" Comparator<Object> lCmp = comparing(o -> 0L);",
" Comparator<Object> fCmp = comparing(o -> 0.0f);",
" Comparator<Object> dCmp = comparing(o -> 0.0);",
"}")
.addOutputLines(
"out/Test.java",
"import static java.util.Comparator.comparing;",
"import static java.util.Comparator.comparingDouble;",
"import static java.util.Comparator.comparingInt;",
"import static java.util.Comparator.comparingLong;",
"",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = comparingInt(o -> (byte) 0);",
" Comparator<Object> cCmp = comparingInt(o -> (char) 0);",
" Comparator<Object> sCmp = comparingInt(o -> (short) 0);",
" Comparator<Object> iCmp = comparingInt(o -> 0);",
" Comparator<Object> lCmp = comparingLong(o -> 0L);",
" Comparator<Object> fCmp = comparingDouble(o -> 0.0f);",
" Comparator<Object> dCmp = comparingDouble(o -> 0.0);",
"}")
.doTest();
}
@Test
public void testReplacementWithBoxedVariantsUsingStaticImports() throws IOException {
refactoringTestHelper
.addInputLines(
"in/Test.java",
"import static java.util.Comparator.comparingDouble;",
"import static java.util.Comparator.comparingInt;",
"import static java.util.Comparator.comparingLong;",
"",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = comparingInt(o -> Byte.valueOf((byte) 0));",
" Comparator<Object> cCmp = comparingInt(o -> Character.valueOf((char) 0));",
" Comparator<Object> sCmp = comparingInt(o -> Short.valueOf((short) 0));",
" Comparator<Object> iCmp = comparingInt(o -> Integer.valueOf(0));",
" Comparator<Object> lCmp = comparingLong(o -> Long.valueOf(0));",
" Comparator<Object> fCmp = comparingDouble(o -> Float.valueOf(0));",
" Comparator<Object> dCmp = comparingDouble(o -> Double.valueOf(0));",
"}")
.addOutputLines(
"out/Test.java",
"import static java.util.Comparator.comparing;",
"import static java.util.Comparator.comparingDouble;",
"import static java.util.Comparator.comparingInt;",
"import static java.util.Comparator.comparingLong;",
"",
"import java.util.Comparator;",
"class Test {",
" Comparator<Object> bCmp = comparing(o -> Byte.valueOf((byte) 0));",
" Comparator<Object> cCmp = comparing(o -> Character.valueOf((char) 0));",
" Comparator<Object> sCmp = comparing(o -> Short.valueOf((short) 0));",
" Comparator<Object> iCmp = comparing(o -> Integer.valueOf(0));",
" Comparator<Object> lCmp = comparing(o -> Long.valueOf(0));",
" Comparator<Object> fCmp = comparing(o -> Float.valueOf(0));",
" Comparator<Object> dCmp = comparing(o -> Double.valueOf(0));",
"}")
.doTest();
}
// The logic for `char` and `short` is exactly analogous to the `byte` case.
@Test
public void testByteComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"class A {",
" {",
" // BUG: Diagnostic contains:",
" Comparator.comparing(this::toPrimitive);",
" Comparator.comparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" Comparator.comparing(o -> (byte) 0);",
" Comparator.comparing(o -> (byte) 0, cmp());",
" Comparator.comparing(this::toBoxed);",
" Comparator.comparing(this::toBoxed, cmp());",
" Comparator.comparing(o -> Byte.valueOf((byte) 0));",
" Comparator.comparing(o -> Byte.valueOf((byte) 0), cmp());",
" Comparator.comparing(toBoxed());",
" Comparator.comparing(toBoxed(), cmp());",
" Comparator.comparingInt(this::toPrimitive);",
" Comparator.comparingInt(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" Comparator.comparingInt(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingInt(o -> Byte.valueOf((byte) 0));",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(o -> Byte.valueOf((byte) 0));",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> Byte.valueOf((byte) 0));",
"",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(this::toPrimitive);",
" cmp().thenComparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(o -> (byte) 0);",
" cmp().thenComparing(o -> (byte) 0, cmp());",
" cmp().thenComparing(this::toBoxed);",
" cmp().thenComparing(this::toBoxed, cmp());",
" cmp().thenComparing(o -> Byte.valueOf((byte) 0));",
" cmp().thenComparing(o -> Byte.valueOf((byte) 0), cmp());",
" cmp().thenComparing(toBoxed());",
" cmp().thenComparing(toBoxed(), cmp());",
" cmp().thenComparingInt(this::toPrimitive);",
" cmp().thenComparingInt(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingInt(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingInt(o -> Byte.valueOf((byte) 0));",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(o -> Byte.valueOf((byte) 0));",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> (byte) 0);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> Byte.valueOf((byte) 0));",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private byte toPrimitive(Object o) { return 0; }",
" private Byte toBoxed(Object o) { return 0; }",
" private Function<Object, Byte> toBoxed() { return o -> 0; }",
"}")
.doTest();
}
@Test
public void testIntComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"import java.util.function.ToIntFunction;",
"class A {",
" {",
" // BUG: Diagnostic contains:",
" Comparator.comparing(this::toPrimitive);",
" Comparator.comparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" Comparator.comparing(o -> 0);",
" Comparator.comparing(o -> 0, cmp());",
" Comparator.comparing(this::toBoxed);",
" Comparator.comparing(this::toBoxed, cmp());",
" Comparator.comparing(o -> Integer.valueOf(0));",
" Comparator.comparing(o -> Integer.valueOf(0), cmp());",
" Comparator.comparing(toBoxed());",
" Comparator.comparing(toBoxed(), cmp());",
" Comparator.comparingInt(this::toPrimitive);",
" Comparator.comparingInt(o -> 0);",
" Comparator.comparingInt(toPrimitive());",
" // BUG: Diagnostic contains:",
" Comparator.comparingInt(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingInt(o -> Integer.valueOf(0));",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(o -> 0);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(o -> Integer.valueOf(0));",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> 0);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> Integer.valueOf(0));",
"",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(this::toPrimitive);",
" cmp().thenComparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(o -> 0);",
" cmp().thenComparing(o -> 0, cmp());",
" cmp().thenComparing(this::toBoxed);",
" cmp().thenComparing(this::toBoxed, cmp());",
" cmp().thenComparing(o -> Integer.valueOf(0));",
" cmp().thenComparing(o -> Integer.valueOf(0), cmp());",
" cmp().thenComparing(toBoxed());",
" cmp().thenComparing(toBoxed(), cmp());",
" cmp().thenComparingInt(this::toPrimitive);",
" cmp().thenComparingInt(o -> 0);",
" cmp().thenComparingInt(toPrimitive());",
" // BUG: Diagnostic contains:",
" cmp().thenComparingInt(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingInt(o -> Integer.valueOf(0));",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(o -> 0);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(o -> Integer.valueOf(0));",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> 0);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> Integer.valueOf(0));",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private int toPrimitive(Object o) { return 0; }",
" private Integer toBoxed(Object o) { return 0; }",
" private Function<Object, Integer> toBoxed() { return o -> 0; }",
" private ToIntFunction<Object> toPrimitive() { return o -> 0; }",
"}")
.doTest();
}
@Test
public void testLongComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"import java.util.function.ToLongFunction;",
"class A {",
" {",
" // BUG: Diagnostic contains:",
" Comparator.comparing(this::toPrimitive);",
" Comparator.comparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" Comparator.comparing(o -> 0L);",
" Comparator.comparing(o -> 0L, cmp());",
" Comparator.comparing(this::toBoxed);",
" Comparator.comparing(this::toBoxed, cmp());",
" Comparator.comparing(o -> Long.valueOf(0));",
" Comparator.comparing(o -> Long.valueOf(0), cmp());",
" Comparator.comparing(toBoxed());",
" Comparator.comparing(toBoxed(), cmp());",
" Comparator.comparingLong(this::toPrimitive);",
" Comparator.comparingLong(o -> 0L);",
" Comparator.comparingLong(toPrimitive());",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingLong(o -> Long.valueOf(0));",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> 0L);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> Long.valueOf(0));",
"",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(this::toPrimitive);",
" cmp().thenComparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(o -> 0L);",
" cmp().thenComparing(o -> 0L, cmp());",
" cmp().thenComparing(this::toBoxed);",
" cmp().thenComparing(this::toBoxed, cmp());",
" cmp().thenComparing(o -> Long.valueOf(0));",
" cmp().thenComparing(o -> Long.valueOf(0), cmp());",
" cmp().thenComparing(toBoxed());",
" cmp().thenComparing(toBoxed(), cmp());",
" cmp().thenComparingLong(this::toPrimitive);",
" cmp().thenComparingLong(o -> 0L);",
" cmp().thenComparingLong(toPrimitive());",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingLong(o -> Long.valueOf(0));",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toPrimitive);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> 0L);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> Long.valueOf(0));",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private long toPrimitive(Object o) { return 0L; }",
" private Long toBoxed(Object o) { return 0L; }",
" private Function<Object, Long> toBoxed() { return o -> 0L; }",
" private ToLongFunction<Object> toPrimitive() { return o -> 0L; }",
"}")
.doTest();
}
@Test
public void testFloatComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"class A {",
" {",
" // BUG: Diagnostic contains:",
" Comparator.comparing(this::toPrimitive);",
" Comparator.comparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" Comparator.comparing(o -> 0.0f);",
" Comparator.comparing(o -> 0.0f, cmp());",
" Comparator.comparing(this::toBoxed);",
" Comparator.comparing(this::toBoxed, cmp());",
" Comparator.comparing(o -> Float.valueOf(0));",
" Comparator.comparing(o -> Float.valueOf(0), cmp());",
" Comparator.comparing(toBoxed());",
" Comparator.comparing(toBoxed(), cmp());",
" Comparator.comparingDouble(this::toPrimitive);",
" Comparator.comparingDouble(o -> 0.0f);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> Float.valueOf(0));",
"",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(this::toPrimitive);",
" cmp().thenComparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(o -> 0.0f);",
" cmp().thenComparing(o -> 0.0f, cmp());",
" cmp().thenComparing(this::toBoxed);",
" cmp().thenComparing(this::toBoxed, cmp());",
" cmp().thenComparing(o -> Float.valueOf(0));",
" cmp().thenComparing(o -> Float.valueOf(0), cmp());",
" cmp().thenComparing(toBoxed());",
" cmp().thenComparing(toBoxed(), cmp());",
" cmp().thenComparingDouble(this::toPrimitive);",
" cmp().thenComparingDouble(o -> 0.0f);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> Float.valueOf(0));",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private float toPrimitive(Object o) { return 0.0f; }",
" private Float toBoxed(Object o) { return 0.0f; }",
" private Function<Object, Float> toBoxed() { return o -> 0.0f; }",
"}")
.doTest();
}
@Test
public void testDoubleComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"import java.util.function.ToDoubleFunction;",
"class A {",
" {",
" // BUG: Diagnostic contains:",
" Comparator.comparing(this::toPrimitive);",
" Comparator.comparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" Comparator.comparing(o -> 0.0);",
" Comparator.comparing(o -> 0.0, cmp());",
" Comparator.comparing(this::toBoxed);",
" Comparator.comparing(this::toBoxed, cmp());",
" Comparator.comparing(o -> Double.valueOf(0));",
" Comparator.comparing(o -> Double.valueOf(0), cmp());",
" Comparator.comparing(toBoxed());",
" Comparator.comparing(toBoxed(), cmp());",
" Comparator.comparingDouble(this::toPrimitive);",
" Comparator.comparingDouble(o -> 0.0);",
" Comparator.comparingDouble(toPrimitive());",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" Comparator.comparingDouble(o -> Double.valueOf(0));",
"",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(this::toPrimitive);",
" cmp().thenComparing(this::toPrimitive, cmp());",
" // BUG: Diagnostic contains:",
" cmp().thenComparing(o -> 0.0);",
" cmp().thenComparing(o -> 0.0, cmp());",
" cmp().thenComparing(this::toBoxed);",
" cmp().thenComparing(this::toBoxed, cmp());",
" cmp().thenComparing(o -> Double.valueOf(0));",
" cmp().thenComparing(o -> Double.valueOf(0), cmp());",
" cmp().thenComparing(toBoxed());",
" cmp().thenComparing(toBoxed(), cmp());",
" cmp().thenComparingDouble(this::toPrimitive);",
" cmp().thenComparingDouble(o -> 0.0);",
" cmp().thenComparingDouble(toPrimitive());",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(this::toBoxed);",
" // BUG: Diagnostic contains:",
" cmp().thenComparingDouble(o -> Double.valueOf(0));",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private double toPrimitive(Object o) { return 0.0; }",
" private Double toBoxed(Object o) { return 0.0; }",
" private Function<Object, Double> toBoxed() { return o -> 0.0; }",
" private ToDoubleFunction<Object> toPrimitive() { return o -> 0.0; }",
"}")
.doTest();
}
@Test
public void testStringComparison() {
compilationTestHelper
.addSourceLines(
"A.java",
"import java.util.Comparator;",
"import java.util.function.Function;",
"class A {",
" {",
" Comparator.comparing(String::valueOf);",
" Comparator.comparing(String::valueOf, cmp());",
" Comparator.comparing(o -> String.valueOf(o));",
" Comparator.comparing(o -> String.valueOf(o), cmp());",
" Comparator.comparing(toStr());",
" Comparator.comparing(toStr(), cmp());",
"",
" cmp().thenComparing(String::valueOf);",
" cmp().thenComparing(String::valueOf, cmp());",
" cmp().thenComparing(o -> String.valueOf(o));",
" cmp().thenComparing(o -> String.valueOf(o), cmp());",
" cmp().thenComparing(toStr());",
" cmp().thenComparing(toStr(), cmp());",
" }",
"",
" private Comparator<Object> cmp() { return null; }",
" private Function<Object, String> toStr() { return String::valueOf; }",
"}")
.doTest();
}
}

View File

@@ -0,0 +1,48 @@
package com.picnicinternational.errorprone.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class EmptyMethodCheckTest {
private final CompilationTestHelper testHelper =
CompilationTestHelper.newInstance(EmptyMethodCheck.class, getClass());
@Test
public void testNegative() {
testHelper
.addSourceLines(
"A.java",
"class A {",
" Object m() {",
" return null;",
" }",
"",
" void m2() {",
" System.out.println(42);",
" }",
"",
" interface F {",
" void fun();",
" }",
"}")
.doTest();
}
@Test
public void testPositive() {
testHelper
.addSourceLines(
"A.java",
"class A {",
" // BUG: Diagnostic contains:",
" void m() {}",
"",
" // BUG: Diagnostic contains:",
" static void m2() {}",
"}")
.doTest();
}
}