Skip to content

Commit

Permalink
CORE-97 Add MissingImmutableSortedSetDefaultCheck
Browse files Browse the repository at this point in the history
  • Loading branch information
ferdinand-swoboda committed May 18, 2022
1 parent 3edc483 commit 7cd1477
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 0 deletions.
4 changes: 4 additions & 0 deletions error-prone-contrib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Tree> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"interface B {",
" // BUG: Diagnostic matches: X",
" ImmutableSortedSet<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Immutable",
"abstract class C {",
" // BUG: Diagnostic matches: X",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"abstract class D {",
" // BUG: Diagnostic matches: X",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> 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<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"interface B {",
" ImmutableSortedSet<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Immutable",
"abstract class C {",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"abstract class D {",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> 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<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"interface B {",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> sortedSet();",
" default ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" default ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Immutable",
"abstract class C {",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> defaultSortedSet3();",
"}",
"",
"@Value.Modifiable",
"abstract class D {",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> sortedSet();",
" ImmutableSortedSet<String> defaultSortedSet() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.Default",
" ImmutableSortedSet<String> defaultSortedSet2() {",
" return ImmutableSortedSet.of();",
" }",
" @Value.NaturalOrder",
" abstract ImmutableSortedSet<String> defaultSortedSet3();",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@
<artifactId>hamcrest-library</artifactId>
<version>${version.hamcrest}</version>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
Expand Down

0 comments on commit 7cd1477

Please sign in to comment.