diff --git a/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldName.java b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldName.java
new file mode 100644
index 00000000000..c2472920a2f
--- /dev/null
+++ b/error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldName.java
@@ -0,0 +1,114 @@
+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.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+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.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import java.util.Locale;
+import javax.inject.Inject;
+import javax.lang.model.element.Modifier;
+import tech.picnic.errorprone.bugpatterns.util.Flags;
+
+/**
+ * A {@link BugChecker} that warns and suggests the fix when a constant variable does not follow the
+ * upper snake case naming convention.
+ *
+ *
Example:
+ *
+ *
This checker will re-write the following variables with all its references:
+ *
+ *
+ * - private static final int number = 1;
+ *
- static final int otherNumber = 2;
+ *
+ *
+ * To the following:
+ *
+ *
+ * - private static final int NUMBER = 1;
+ *
- static final int OTHER_NUMBER = 2;
+ *
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ summary =
+ "Make sure that all constant variables follow the `UPPER_SNAKE_CASE` naming convention.",
+ link = BUG_PATTERNS_BASE_URL + "CanonicalConstantFieldName",
+ linkType = CUSTOM,
+ severity = WARNING,
+ tags = LIKELY_ERROR)
+public final class CanonicalConstantFieldName 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 String EXCLUDED_CONSTANT_FIELD_NAMES =
+ "CanonicalConstantFieldName:ExcludedConstantFliedNames";
+
+ private final ImmutableList excludedConstantFliedNames;
+
+ /** Instantiates a default {@link CanonicalConstantFieldName} instance. */
+ public CanonicalConstantFieldName() {
+ this(ErrorProneFlags.empty());
+ }
+
+ /**
+ * Instantiates a customized {@link CanonicalConstantFieldName}.
+ *
+ * @param flags Any provided command line flags.
+ */
+ @Inject
+ CanonicalConstantFieldName(ErrorProneFlags flags) {
+ excludedConstantFliedNames = getCanonicalizedLoggerName(flags);
+ }
+
+ @Override
+ public Description matchVariable(VariableTree tree, VisitorState state) {
+ if (!IS_CONSTANT.matches(tree, state)) {
+ return Description.NO_MATCH;
+ }
+ SuggestedFix.Builder fixBuilder = SuggestedFix.builder();
+
+ VarSymbol variableSymbol = ASTHelpers.getSymbol(tree);
+ String variableName = variableSymbol.getSimpleName().toString();
+
+ if (!isVariableUpperSnakeCase(variableName) && !isVariableNameExcluded(variableName)) {
+ fixBuilder.merge(SuggestedFixes.renameVariable(tree, toUpperSnakeCase(variableName), state));
+ }
+
+ return fixBuilder.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fixBuilder.build());
+ }
+
+ private static boolean isVariableUpperSnakeCase(String variableName) {
+ return variableName.equals(toUpperSnakeCase(variableName));
+ }
+
+ private boolean isVariableNameExcluded(String variableName) {
+ return excludedConstantFliedNames.contains(variableName);
+ }
+
+ private static String toUpperSnakeCase(String variableName) {
+ return variableName.replaceAll("([a-z])([A-Z])", "$1_$2").toUpperCase(Locale.ROOT);
+ }
+
+ private static ImmutableList getCanonicalizedLoggerName(ErrorProneFlags flags) {
+ return Flags.getList(flags, EXCLUDED_CONSTANT_FIELD_NAMES);
+ }
+}
diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldNameTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldNameTest.java
new file mode 100644
index 00000000000..56dadbcc51a
--- /dev/null
+++ b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/CanonicalConstantFieldNameTest.java
@@ -0,0 +1,39 @@
+package tech.picnic.errorprone.bugpatterns;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
+import org.junit.jupiter.api.Test;
+
+final class CanonicalConstantFieldNameTest {
+ @Test
+ void replacement() {
+ BugCheckerRefactoringTestHelper.newInstance(CanonicalConstantFieldName.class, getClass())
+ .addInputLines(
+ "A.java",
+ "class A {",
+ " private static final int number = 1;",
+ "",
+ " static final int otherNumber = 2;",
+ "",
+ " static final int ANOTHER_NUMBER = 3;",
+ "",
+ " static int getNumber() {",
+ " return number;",
+ " }",
+ "}")
+ .addOutputLines(
+ "A.java",
+ "class A {",
+ " private static final int NUMBER = 1;",
+ "",
+ " static final int OTHER_NUMBER = 2;",
+ "",
+ " static final int ANOTHER_NUMBER = 3;",
+ "",
+ " static int getNumber() {",
+ " return NUMBER;",
+ " }",
+ "}")
+ .doTest(TestMode.TEXT_MATCH);
+ }
+}
diff --git a/pom.xml b/pom.xml
index f073c04cc19..b1e69ec2cb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1732,6 +1732,7 @@
readable than the alternative. -->
-Xep:YodaCondition:OFF
-XepOpt:CheckReturnValue:CheckAllConstructors=true
+ -XepOpt:CanonicalConstantFieldName:ExcludedConstantFliedNames=serialVersionUID