diff --git a/error-prone-contrib/pom.xml b/error-prone-contrib/pom.xml
index a49552a2d0e..a0dd1e7ada6 100644
--- a/error-prone-contrib/pom.xml
+++ b/error-prone-contrib/pom.xml
@@ -141,6 +141,10 @@
hamcrest
test
+
+ org.immutables
+ value
+
org.junit.jupiter
junit-jupiter-api
diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheck.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheck.java
new file mode 100644
index 00000000000..df52fabe395
--- /dev/null
+++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheck.java
@@ -0,0 +1,69 @@
+package tech.picnic.errorprone.bugpatterns;
+
+import static com.google.errorprone.BugPattern.LinkType.NONE;
+import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
+import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
+import static com.google.errorprone.matchers.Matchers.hasAnnotation;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import org.immutables.value.Value;
+
+/**
+ * A {@link BugChecker} which flags methods with return type {@link
+ * com.google.common.collect.ImmutableSortedSet} within an {@code @Value.Immutable} or
+ * {@code @Value.Modifiable} class that lack either a default implementation or
+ * {@code @Value.NaturalOrder}.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "MissingImmutableSortedSetDefault",
+ summary =
+ "Properties of type `ImmutableSortedSet` within an @Value.Immutable or @Value.Modifiable class "
+ + "should provide a default value or specify the comparator.",
+ linkType = NONE,
+ severity = ERROR,
+ tags = LIKELY_ERROR)
+public final class MissingImmutableSortedSetDefaultCheck extends BugChecker
+ implements MethodTreeMatcher {
+ private static final long serialVersionUID = 1L;
+ private static final String VALUE_NATURAL_ORDER_ANNOTATION =
+ "org.immutables.value.Value.NaturalOrder";
+ private static final Matcher HAS_NATURAL_ORDER =
+ hasAnnotation(VALUE_NATURAL_ORDER_ANNOTATION);
+
+ @Override
+ public Description matchMethod(MethodTree tree, VisitorState state) {
+ // is not within immutable or modifiable class -> no match
+ if (tree.getClass().isAnnotationPresent(org.immutables.value.Value.Immutable.class)
+ || tree.getClass().isAnnotationPresent(Value.Modifiable.class)) {
+ return Description.NO_MATCH;
+ }
+
+ // has implementation -> no match
+ if (tree.getDefaultValue() != null) {
+ return Description.NO_MATCH;
+ }
+
+ // is annotated with @Value.NaturalOrder -> no match
+ if (HAS_NATURAL_ORDER.matches(tree, state)) {
+ return Description.NO_MATCH;
+ }
+
+ // The ImmutableSortedSet has no empty default -> add the `@Value.NaturalOrder` annotation.
+ return describeMatch(
+ tree,
+ SuggestedFix.builder()
+ .addStaticImport(VALUE_NATURAL_ORDER_ANNOTATION)
+ .prefixWith(tree, "@Value.NaturalOrder ")
+ .build());
+ }
+}
diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheckTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheckTest.java
new file mode 100644
index 00000000000..c612088e016
--- /dev/null
+++ b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/MissingImmutableSortedSetDefaultCheckTest.java
@@ -0,0 +1,223 @@
+package tech.picnic.errorprone.bugpatterns;
+
+import static com.google.common.base.Predicates.containsPattern;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
+import com.google.errorprone.CompilationTestHelper;
+import org.junit.jupiter.api.Test;
+
+public final class MissingImmutableSortedSetDefaultCheckTest {
+ private final CompilationTestHelper compilationTestHelper =
+ CompilationTestHelper.newInstance(MissingImmutableSortedSetDefaultCheck.class, getClass())
+ .expectErrorMessage(
+ "X",
+ containsPattern(
+ "Properties of type `ImmutableSortedSet` within an @Value.Immutable or @Value.Modifiable class should provide a default value or specify the comparator."));
+ private final BugCheckerRefactoringTestHelper refactoringTestHelper =
+ BugCheckerRefactoringTestHelper.newInstance(
+ MissingImmutableSortedSetDefaultCheck.class, getClass());
+
+ @Test
+ void identification() {
+ compilationTestHelper
+ .addSourceLines(
+ "A.java",
+ "import org.immutables.value.Value;",
+ "import com.google.common.collect.ImmutableSet;",
+ "import com.google.common.collect.ImmutableSortedSet;",
+ "",
+ "@Value.Immutable",
+ "interface A {",
+ " // BUG: Diagnostic matches: X",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "interface B {",
+ " // BUG: Diagnostic matches: X",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Immutable",
+ "abstract class C {",
+ " // BUG: Diagnostic matches: X",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "abstract class D {",
+ " // BUG: Diagnostic matches: X",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ void replacement() {
+ refactoringTestHelper
+ .addInputLines(
+ "A.java",
+ "import org.immutables.value.Value;",
+ "import com.google.common.collect.ImmutableSet;",
+ "import com.google.common.collect.ImmutableSortedSet;",
+ "",
+ "@Value.Immutable",
+ "interface A {",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "interface B {",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Immutable",
+ "abstract class C {",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "abstract class D {",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}")
+ .addOutputLines(
+ "A.java",
+ "import org.immutables.value.Value;",
+ "import com.google.common.collect.ImmutableSet;",
+ "import com.google.common.collect.ImmutableSortedSet;",
+ "",
+ "@Value.Immutable",
+ "interface A {",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "interface B {",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet sortedSet();",
+ " default ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " default ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Immutable",
+ "abstract class C {",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}",
+ "",
+ "@Value.Modifiable",
+ "abstract class D {",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet sortedSet();",
+ " ImmutableSortedSet defaultSortedSet() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.Default",
+ " ImmutableSortedSet defaultSortedSet2() {",
+ " return ImmutableSortedSet.of();",
+ " }",
+ " @Value.NaturalOrder",
+ " abstract ImmutableSortedSet defaultSortedSet3();",
+ "}")
+ .doTest(TestMode.TEXT_MATCH);
+ }
+}
diff --git a/pom.xml b/pom.xml
index d7134064da8..561e4961dd3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -364,6 +364,11 @@
hamcrest-library
${version.hamcrest}
+
+ org.immutables
+ value
+ 2.8.2
+
org.junit
junit-bom