diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNaming.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNaming.java deleted file mode 100644 index 0e5afd8d9c0..00000000000 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNaming.java +++ /dev/null @@ -1,157 +0,0 @@ -package tech.picnic.errorprone.bugpatterns; - -import static com.google.errorprone.BugPattern.LinkType.CUSTOM; -import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; -import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR; -import static com.google.errorprone.matchers.Matchers.allOf; -import static com.google.errorprone.matchers.Matchers.hasModifier; -import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL; - -import com.google.auto.service.AutoService; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.errorprone.BugPattern; -import com.google.errorprone.ErrorProneFlags; -import com.google.errorprone.VisitorState; -import com.google.errorprone.bugpatterns.BugChecker; -import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher; -import com.google.errorprone.fixes.SuggestedFix; -import com.google.errorprone.fixes.SuggestedFixes; -import com.google.errorprone.matchers.Description; -import com.google.errorprone.matchers.Matcher; -import com.google.errorprone.util.ASTHelpers; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.VariableTree; -import com.sun.source.util.TreeScanner; -import java.util.Locale; -import java.util.regex.Pattern; -import javax.inject.Inject; -import javax.lang.model.element.Modifier; -import org.jspecify.annotations.Nullable; -import tech.picnic.errorprone.utils.Flags; - -/** - * A {@link BugChecker} that flags constant variables that do not follow the upper snake case naming - * convention. - * - *

This check will rewrite the following variables with all its references: - * - *

{@code
- * private static final int simpleNumber = 1;
- * }
- * - *

To the following: - * - *

{@code
- * private static final int SIMPLE_NUMBER = 1;
- * }
- * - * @apiNote This check has an optional flag `AllowedConstantNames` that represents a list of field - * names to exclude from this check. - */ -@AutoService(BugChecker.class) -@BugPattern( - summary = "Constant variables should adhere to the `UPPER_SNAKE_CASE` naming convention", - link = BUG_PATTERNS_BASE_URL + "CanonicalConstantNaming", - linkType = CUSTOM, - severity = WARNING, - tags = LIKELY_ERROR) -public final class CanonicalConstantNaming extends BugChecker implements VariableTreeMatcher { - private static final long serialVersionUID = 1L; - private static final Matcher IS_CONSTANT = - allOf( - hasModifier(Modifier.STATIC), hasModifier(Modifier.PRIVATE), hasModifier(Modifier.FINAL)); - private static final Pattern SNAKE_CASE = Pattern.compile("([a-z])([A-Z])"); - private static final ImmutableSet DEFAULT_ALLOWED_CONSTANT_NAMES = - ImmutableSet.of("serialVersionUID"); - private static final String ALLOWED_CONSTANT_NAMES_FLAG = - "CanonicalConstantNaming:AllowedConstantNames"; - - private final ImmutableList allowedConstantNames; - - /** Instantiates a default {@link CanonicalConstantNaming} instance. */ - public CanonicalConstantNaming() { - this(ErrorProneFlags.empty()); - } - - /** - * Instantiates a customized {@link CanonicalConstantNaming}. - * - * @param flags Any provided command line flags. - */ - @Inject - CanonicalConstantNaming(ErrorProneFlags flags) { - allowedConstantNames = getAllowedFieldNames(flags); - } - - @Override - public Description matchVariable(VariableTree tree, VisitorState state) { - String variableName = tree.getName().toString(); - if (!IS_CONSTANT.matches(tree, state) - || isUpperSnakeCase(variableName) - || isVariableNameAllowed(variableName)) { - return Description.NO_MATCH; - } - - SuggestedFix.Builder fixBuilder = SuggestedFix.builder(); - - ImmutableList variablesInCompilationUnit = - getVariablesInCompilationUnit(state.getPath().getCompilationUnit()); - String replacement = toUpperSnakeCase(variableName); - if (isVariableNameInUse(variablesInCompilationUnit, replacement)) { - reportConstantRenameBlocker(tree, replacement, state); - } else { - fixBuilder.merge(SuggestedFixes.renameVariable(tree, replacement, state)); - } - - return describeMatch(tree, fixBuilder.build()); - } - - private static ImmutableList getAllowedFieldNames(ErrorProneFlags flags) { - return Flags.getList(flags, ALLOWED_CONSTANT_NAMES_FLAG); - } - - private static boolean isUpperSnakeCase(String name) { - return name.contentEquals(toUpperSnakeCase(name)); - } - - private boolean isVariableNameAllowed(String variableName) { - return allowedConstantNames.contains(variableName) - || DEFAULT_ALLOWED_CONSTANT_NAMES.contains(variableName); - } - - private static ImmutableList getVariablesInCompilationUnit( - CompilationUnitTree tree) { - ImmutableList.Builder variablesInFileBuilder = ImmutableList.builder(); - new TreeScanner<@Nullable Void, @Nullable Void>() { - @Override - public @Nullable Void visitVariable(VariableTree variableTree, @Nullable Void unused) { - variablesInFileBuilder.add(variableTree); - return super.visitVariable(variableTree, null); - } - }.scan(tree, null); - - return variablesInFileBuilder.build(); - } - - private static String toUpperSnakeCase(String variableName) { - return SNAKE_CASE.matcher(variableName).replaceAll("$1_$2").toUpperCase(Locale.ROOT); - } - - private static boolean isVariableNameInUse( - ImmutableList variablesInCompilationUnit, String replacement) { - return variablesInCompilationUnit.stream() - .map(ASTHelpers::getSymbol) - .anyMatch(s -> s.getSimpleName().toString().equals(replacement)); - } - - private void reportConstantRenameBlocker( - VariableTree tree, String replacement, VisitorState state) { - state.reportMatch( - buildDescription(tree) - .setMessage( - String.format( - "a variable named `%s` is already defined in this scope", replacement)) - .build()); - } -} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ConstantNaming.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ConstantNaming.java new file mode 100644 index 00000000000..a0354a83c61 --- /dev/null +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ConstantNaming.java @@ -0,0 +1,125 @@ +package tech.picnic.errorprone.bugpatterns; + +import static com.google.errorprone.BugPattern.LinkType.CUSTOM; +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.BugPattern.StandardTags.STYLE; +import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.hasModifier; +import static tech.picnic.errorprone.utils.Documentation.BUG_PATTERNS_BASE_URL; + +import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.errorprone.BugPattern; +import com.google.errorprone.ErrorProneFlags; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.VariableTreeMatcher; +import com.google.errorprone.fixes.SuggestedFixes; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.util.ASTHelpers; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreeScanner; +import java.util.Locale; +import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.lang.model.element.Modifier; +import org.jspecify.annotations.Nullable; +import tech.picnic.errorprone.utils.Flags; + +/** + * A {@link BugChecker} that flags static constants that do not follow the upper snake case naming + * convention. + */ +@AutoService(BugChecker.class) +@BugPattern( + summary = "Constant variables should adhere to the `UPPER_SNAKE_CASE` naming convention", + link = BUG_PATTERNS_BASE_URL + "ConstantNaming", + linkType = CUSTOM, + severity = WARNING, + tags = STYLE) +@SuppressWarnings("java:S2160" /* Super class equality definition suffices. */) +public final class ConstantNaming extends BugChecker implements VariableTreeMatcher { + private static final long serialVersionUID = 1L; + private static final Matcher IS_CONSTANT = + allOf(hasModifier(Modifier.STATIC), hasModifier(Modifier.FINAL)); + private static final Matcher IS_PRIVATE = hasModifier(Modifier.PRIVATE); + private static final Pattern SNAKE_CASE = Pattern.compile("([a-z])([A-Z])"); + private static final ImmutableSet DEFAULT_EXEMPTED_NAMES = + ImmutableSet.of("serialVersionUID"); + + /** + * Flag using which constant names that must not be flagged (in addition to those defined by + * {@link #DEFAULT_EXEMPTED_NAMES}) can be specified. + */ + private static final String ADDITIONAL_EXEMPTED_NAMES_FLAG = + "CanonicalConstantNaming:ExemptedNames"; + + private final ImmutableSet exemptedNames; + + /** Instantiates a default {@link ConstantNaming} instance. */ + public ConstantNaming() { + this(ErrorProneFlags.empty()); + } + + /** + * Instantiates a customized {@link ConstantNaming}. + * + * @param flags Any provided command line flags. + */ + @Inject + ConstantNaming(ErrorProneFlags flags) { + exemptedNames = + Sets.union(DEFAULT_EXEMPTED_NAMES, Flags.getSet(flags, ADDITIONAL_EXEMPTED_NAMES_FLAG)) + .immutableCopy(); + } + + @Override + public Description matchVariable(VariableTree tree, VisitorState state) { + String variableName = tree.getName().toString(); + if (!IS_CONSTANT.matches(tree, state) || exemptedNames.contains(variableName)) { + return Description.NO_MATCH; + } + + String replacement = toUpperSnakeCase(variableName); + if (replacement.equals(variableName)) { + return Description.NO_MATCH; + } + + Description.Builder description = buildDescription(tree); + if (!IS_PRIVATE.matches(tree, state)) { + description.setMessage( + "%s; consider renaming to '%s', though note that this is not a private constant" + .formatted(message(), replacement)); + } else if (isVariableNameInUse(replacement, state)) { + description.setMessage( + "%s; consider renaming to '%s', though note that a variable with this name is already declared" + .formatted(message(), replacement)); + } else { + description.addFix(SuggestedFixes.renameVariable(tree, replacement, state)); + } + + return description.build(); + } + + private static String toUpperSnakeCase(String variableName) { + return SNAKE_CASE.matcher(variableName).replaceAll("$1_$2").toUpperCase(Locale.ROOT); + } + + private static boolean isVariableNameInUse(String name, VisitorState state) { + return Boolean.TRUE.equals( + new TreeScanner() { + @Override + public Boolean visitVariable(VariableTree tree, @Nullable Void unused) { + return ASTHelpers.getSymbol(tree).getSimpleName().toString().equals(name) + || super.visitVariable(tree, null); + } + + @Override + public Boolean reduce(Boolean r1, Boolean r2) { + return Boolean.TRUE.equals(r1) || Boolean.TRUE.equals(r2); + } + }.scan(state.getPath().getCompilationUnit(), null)); + } +} diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java index fca196b4647..a788c6991fc 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalAnnotationAttributeListing.java @@ -14,6 +14,7 @@ import com.google.common.collect.Comparators; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import com.google.errorprone.BugPattern; import com.google.errorprone.ErrorProneFlags; import com.google.errorprone.VisitorState; @@ -34,10 +35,8 @@ import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.stream.Stream; import javax.inject.Inject; import org.jspecify.annotations.Nullable; @@ -230,10 +229,8 @@ private static AnnotationAttributeMatcher createAnnotationAttributeMatcher( excludedAnnotations(flags)); } - private static ImmutableList excludedAnnotations(ErrorProneFlags flags) { - Set exclusions = new HashSet<>(); - exclusions.addAll(Flags.getList(flags, EXCLUDED_ANNOTATIONS_FLAG)); - exclusions.addAll(BLACKLISTED_ANNOTATIONS); - return ImmutableList.copyOf(exclusions); + private static ImmutableSet excludedAnnotations(ErrorProneFlags flags) { + return Sets.union(BLACKLISTED_ANNOTATIONS, Flags.getSet(flags, EXCLUDED_ANNOTATIONS_FLAG)) + .immutableCopy(); } } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java index bc6dadbd2f7..689996a06c9 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RedundantStringConversion.java @@ -378,11 +378,11 @@ private Description createDescription(Tree tree, Optional private static Matcher createConversionMethodMatcher( ErrorProneFlags flags) { - // XXX: ErrorProneFlags#getList splits by comma, but method signatures may also contain commas. - // For this class methods accepting more than one argument are not valid, but still: not nice. + // XXX: `Flags#getSet` splits by comma, but method signatures may also contain commas. For this + // class methods accepting more than one argument are not valid, but still: not nice. return anyOf( WELL_KNOWN_STRING_CONVERSION_METHODS, new MethodMatcherFactory() - .create(Flags.getList(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG))); + .create(Flags.getSet(flags, EXTRA_STRING_CONVERSION_METHODS_FLAG))); } } diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java index ee736da113a..3d2aef0c924 100644 --- a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java +++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/RequestParamType.java @@ -15,8 +15,8 @@ import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.errorprone.BugPattern; import com.google.errorprone.ErrorProneFlags; import com.google.errorprone.VisitorState; @@ -74,10 +74,10 @@ private static Matcher hasUnsupportedRequestParamType(ErrorProneFl return allOf( annotations(AT_LEAST_ONE, isType("org.springframework.web.bind.annotation.RequestParam")), anyOf(isSubtypeOf(ImmutableCollection.class), isSubtypeOf(ImmutableMap.class)), - not(isSubtypeOfAny(Flags.getList(flags, SUPPORTED_CUSTOM_TYPES_FLAG)))); + not(isSubtypeOfAny(Flags.getSet(flags, SUPPORTED_CUSTOM_TYPES_FLAG)))); } - private static Matcher isSubtypeOfAny(ImmutableList inclusions) { + private static Matcher isSubtypeOfAny(ImmutableSet inclusions) { return anyOf( inclusions.stream() .map(inclusion -> isSubtypeOf(Suppliers.typeFromString(inclusion))) diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNamingTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNamingTest.java deleted file mode 100644 index 4ddd2f00d98..00000000000 --- a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantNamingTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package tech.picnic.errorprone.bugpatterns; - -import com.google.errorprone.BugCheckerRefactoringTestHelper; -import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; -import com.google.errorprone.CompilationTestHelper; -import org.junit.jupiter.api.Test; - -final class CanonicalConstantNamingTest { - @Test - void identification() { - CompilationTestHelper.newInstance(CanonicalConstantNaming.class, getClass()) - .addSourceLines( - "A.java", - "class A {", - " // BUG: Diagnostic contains:", - " private static final int foo = 1;", - "", - " // BUG: Diagnostic contains:", - " private static final int bar_BAR = 2;", - "", - " private final int baz = 2;", - "", - " public static final int qux = 3;", - "", - " static final int quux = 4;", - "", - " private final int quuz = 5;", - "", - " int corge = 6;", - "", - " private static final long serialVersionUID = 1L;", - "", - " // BUG: Diagnostic contains: a variable named `NUMBER` is already defined in this scope", - " private static final int number = B.NUMBER;", - "", - " class B {", - " private static final int NUMBER = 1;", - "", - " // BUG: Diagnostic contains:", - " private static final int foo = 2;", - " }", - "}") - .doTest(); - } - - @Test - void replacement() { - BugCheckerRefactoringTestHelper.newInstance(CanonicalConstantNaming.class, getClass()) - .addInputLines( - "A.java", - "class A {", - " private static final int number = 1;", - "", - " int referenceToNumberFromAnotherClass = B.numberFromAnotherClass;", - "", - " static int getConstantNumber() {", - " return number;", - " }", - "", - " static int getLocalNumber() {", - " int number = 3;", - "", - " return number;", - " }", - "", - " class B {", - " private static final int number = 4;", - "", - " private static final int numberFromAnotherClass = 5;", - " }", - "}") - .addOutputLines( - "A.java", - "class A {", - " private static final int NUMBER = 1;", - "", - " int referenceToNumberFromAnotherClass = B.NUMBER_FROM_ANOTHER_CLASS;", - "", - " static int getConstantNumber() {", - " return NUMBER;", - " }", - "", - " static int getLocalNumber() {", - " int number = 3;", - "", - " return number;", - " }", - "", - " class B {", - " private static final int NUMBER = 4;", - "", - " private static final int NUMBER_FROM_ANOTHER_CLASS = 5;", - " }", - "}") - .doTest(TestMode.TEXT_MATCH); - } - - @Test - void allowedConstantsFlag() { - BugCheckerRefactoringTestHelper.newInstance(CanonicalConstantNaming.class, getClass()) - .setArgs("-XepOpt:CanonicalConstantNaming:AllowedConstantNames=foo") - .addInputLines( - "A.java", - "class A {", - " private static final int number = 1;", - "", - " private static final int foo = 3;", - "}") - .addOutputLines( - "A.java", - "class A {", - " private static final int NUMBER = 1;", - "", - " private static final int foo = 3;", - "}") - .doTest(TestMode.TEXT_MATCH); - } -} diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/ConstantNamingTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/ConstantNamingTest.java new file mode 100644 index 00000000000..889f8599a34 --- /dev/null +++ b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/ConstantNamingTest.java @@ -0,0 +1,78 @@ +package tech.picnic.errorprone.bugpatterns; + +import com.google.errorprone.BugCheckerRefactoringTestHelper; +import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode; +import com.google.errorprone.CompilationTestHelper; +import org.junit.jupiter.api.Test; + +final class ConstantNamingTest { + @Test + void identification() { + CompilationTestHelper.newInstance(ConstantNaming.class, getClass()) + .addSourceLines( + "A.java", + "class A {", + " private static final long serialVersionUID = 1L;", + " private static final int FOO = 1;", + " // BUG: Diagnostic contains: consider renaming to 'BAR', though note that this is not a private", + " // constant", + " static final int bar = 2;", + " // BUG: Diagnostic contains:", + " private static final int baz = 3;", + " // BUG: Diagnostic contains: consider renaming to 'QUX_QUUX', though note that a variable with", + " // this name is already declared", + " private static final int qux_QUUX = 4;", + " // BUG: Diagnostic contains: consider renaming to 'QUUZ', though note that a variable with", + " // this name is already declared", + " private static final int quuz = 3;", + "", + " private final int foo = 4;", + " private final Runnable QUX_QUUX =", + " new Runnable() {", + " private static final int QUUZ = 1;", + "", + " @Override", + " public void run() {}", + " };", + "}") + .doTest(); + } + + @Test + void identificationWithCustomExemption() { + CompilationTestHelper.newInstance(ConstantNaming.class, getClass()) + .setArgs("-XepOpt:CanonicalConstantNaming:ExemptedNames=foo,baz") + .addSourceLines( + "A.java", + "class A {", + " private static final long serialVersionUID = 1L;", + " private static final int foo = 1;", + " // BUG: Diagnostic contains:", + " private static final int bar = 2;", + " private static final int baz = 3;", + "}") + .doTest(); + } + + @Test + void replacement() { + BugCheckerRefactoringTestHelper.newInstance(ConstantNaming.class, getClass()) + .addInputLines( + "A.java", + "class A {", + " static final int foo = 1;", + " private static final int bar = 2;", + " private static final int baz = 3;", + " private static final int BAZ = 4;", + "}") + .addOutputLines( + "A.java", + "class A {", + " static final int foo = 1;", + " private static final int BAR = 2;", + " private static final int baz = 3;", + " private static final int BAZ = 4;", + "}") + .doTest(TestMode.TEXT_MATCH); + } +} diff --git a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/Flags.java b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/Flags.java index df2787c7f40..b8e263404f7 100644 --- a/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/Flags.java +++ b/error-prone-utils/src/main/java/tech/picnic/errorprone/utils/Flags.java @@ -1,6 +1,7 @@ package tech.picnic.errorprone.utils; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.errorprone.ErrorProneFlags; /** Helper methods for working with {@link ErrorProneFlags}. */ @@ -19,4 +20,19 @@ public static ImmutableList getList(ErrorProneFlags errorProneFlags, Str ImmutableList list = errorProneFlags.getListOrEmpty(name); return list.equals(ImmutableList.of("")) ? ImmutableList.of() : list; } + + /** + * Returns the set of (comma-separated) arguments passed using the given Error Prone flag. + * + * @param errorProneFlags The full set of flags provided. + * @param name The name of the flag of interest. + * @return A non-{@code null} set of provided arguments; this set is empty if the flag was not + * provided, or if the flag's value is the empty string. + * @implNote This method does not delegate to {@link ErrorProneFlags#getSetOrEmpty(String)}, as + * that method wouldn't allow us to identify a non-singleton set of empty strings; such a set + * should not be treated as empty. + */ + public static ImmutableSet getSet(ErrorProneFlags errorProneFlags, String name) { + return ImmutableSet.copyOf(getList(errorProneFlags, name)); + } } diff --git a/error-prone-utils/src/test/java/tech/picnic/errorprone/utils/FlagsTest.java b/error-prone-utils/src/test/java/tech/picnic/errorprone/utils/FlagsTest.java index bc7658ce84a..dcc217c8f7c 100644 --- a/error-prone-utils/src/test/java/tech/picnic/errorprone/utils/FlagsTest.java +++ b/error-prone-utils/src/test/java/tech/picnic/errorprone/utils/FlagsTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.errorprone.ErrorProneOptions; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -11,21 +12,29 @@ import org.junit.jupiter.params.provider.MethodSource; final class FlagsTest { - private static Stream getListTestCases() { - /* { args, flag, expected } */ + private static Stream getCollectionTestCases() { + /* { args, flag, listed } */ return Stream.of( arguments(ImmutableList.of(), "Foo", ImmutableList.of()), arguments(ImmutableList.of("-XepOpt:Foo=bar,baz"), "Qux", ImmutableList.of()), arguments(ImmutableList.of("-XepOpt:Foo="), "Foo", ImmutableList.of()), arguments(ImmutableList.of("-XepOpt:Foo=bar"), "Foo", ImmutableList.of("bar")), + arguments(ImmutableList.of("-XepOpt:Foo=bar,bar"), "Foo", ImmutableList.of("bar", "bar")), arguments(ImmutableList.of("-XepOpt:Foo=bar,baz"), "Foo", ImmutableList.of("bar", "baz")), arguments(ImmutableList.of("-XepOpt:Foo=,"), "Foo", ImmutableList.of("", ""))); } - @MethodSource("getListTestCases") + @MethodSource("getCollectionTestCases") @ParameterizedTest - void getList(ImmutableList args, String flag, ImmutableList expected) { + void getList(ImmutableList args, String flag, ImmutableList listed) { assertThat(Flags.getList(ErrorProneOptions.processArgs(args).getFlags(), flag)) - .containsExactlyElementsOf(expected); + .containsExactlyElementsOf(listed); + } + + @MethodSource("getCollectionTestCases") + @ParameterizedTest + void getSet(ImmutableList args, String flag, ImmutableList listed) { + assertThat(Flags.getSet(ErrorProneOptions.processArgs(args).getFlags(), flag)) + .containsExactlyElementsOf(ImmutableSet.copyOf(listed)); } }