From be4e826d0f69c318358769ea9c4814675433ff9a Mon Sep 17 00:00:00 2001 From: Error Prone Team Date: Wed, 3 May 2023 16:48:57 -0700 Subject: [PATCH] Support `@CompileTimeConstant` for enum fields. The support for *class* fields was introduced in unknown commit. This CL merely extends it to enum fields, considering the similarity between them. There is also a feature request b/149290598 filed a few years back. Specifically, this CL aims to support the following use case (taken from b/149290598): ``` public class Example { public enum ExampleEnum { A("a"), B("b"); @CompileTimeConstant final String s; ExampleEnum(@CompileTimeConstant final String s) { this.s = s; } void invokeCtcMethod() { ctcMethod(s); } private void ctcMethod(@CompileTimeConstant String string) {} } } ``` PiperOrigin-RevId: 529230341 --- .../CompileTimeConstantExpressionMatcher.java | 5 +- .../CompileTimeConstantChecker.java | 3 +- .../CompileTimeConstantCheckerTest.java | 70 +++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/check_api/src/main/java/com/google/errorprone/matchers/CompileTimeConstantExpressionMatcher.java b/check_api/src/main/java/com/google/errorprone/matchers/CompileTimeConstantExpressionMatcher.java index 259bbecf162..f98abdac0fd 100644 --- a/check_api/src/main/java/com/google/errorprone/matchers/CompileTimeConstantExpressionMatcher.java +++ b/check_api/src/main/java/com/google/errorprone/matchers/CompileTimeConstantExpressionMatcher.java @@ -131,11 +131,12 @@ protected Boolean defaultAction(Tree node, Void unused) { Symbol.VarSymbol varSymbol = (Symbol.VarSymbol) getSymbol(node); Symbol owner = varSymbol.owner; ElementKind ownerKind = owner.getKind(); - // Check that the identifier is a formal method/constructor parameter or a class + // Check that the identifier is a formal method/constructor parameter, or a class/enum // field. if (ownerKind != ElementKind.METHOD && ownerKind != ElementKind.CONSTRUCTOR - && ownerKind != ElementKind.CLASS) { + && ownerKind != ElementKind.CLASS + && ownerKind != ElementKind.ENUM) { return false; } // Check that the symbol is final diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/CompileTimeConstantChecker.java b/core/src/main/java/com/google/errorprone/bugpatterns/CompileTimeConstantChecker.java index f6aa7ad6ae8..c83e64c4c42 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/CompileTimeConstantChecker.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/CompileTimeConstantChecker.java @@ -238,7 +238,8 @@ public Description matchAssignment(AssignmentTree node, VisitorState state) { if (assignedSymbol == null || assignedSymbol.owner == null) { return Description.NO_MATCH; } - if (assignedSymbol.owner.getKind() != ElementKind.CLASS) { + if (assignedSymbol.owner.getKind() != ElementKind.CLASS + && assignedSymbol.owner.getKind() != ElementKind.ENUM) { return Description.NO_MATCH; } if (!hasCompileTimeConstantAnnotation(state, assignedSymbol)) { diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/CompileTimeConstantCheckerTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/CompileTimeConstantCheckerTest.java index 6f27f96a9aa..44eac0ab581 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/CompileTimeConstantCheckerTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/CompileTimeConstantCheckerTest.java @@ -570,6 +570,76 @@ public void noDiagnostic_whenInvokingMethodWithFinalField() { .doTest(); } + @Test + public void reportsDiagnostic_whenConstantEnumFieldDeclaredWithoutFinal() { + compilationHelper + .addSourceLines( + "Test.java", + "import com.google.errorprone.annotations.CompileTimeConstant;", + "public enum Test {", + " A(\"A\");", + " // BUG: Diagnostic contains: . Did you mean to make 's' final?", + " @CompileTimeConstant String s;", + " Test(@CompileTimeConstant String s) {", + " this.s = s;", + " }", + "}") + .doTest(); + } + + @Test + public void noDiagnostic_whenConstantEnumFieldDeclaredFinal() { + compilationHelper + .addSourceLines( + "Test.java", + "import com.google.errorprone.annotations.CompileTimeConstant;", + "public enum Test {", + " A(\"A\");", + " @CompileTimeConstant final String s;", + " Test(@CompileTimeConstant String s) {", + " this.s = s;", + " }", + "}") + .doTest(); + } + + @Test + public void reportsDiagnostic_whenInitialisingFinalEnumFieldWithNonConstant() { + compilationHelper + .addSourceLines( + "Test.java", + "import com.google.errorprone.annotations.CompileTimeConstant;", + "public enum Test {", + " A(\"A\");", + " @CompileTimeConstant final String s;", + " Test(String s) {", + " // BUG: Diagnostic contains: Non-compile-time constant expression", + " this.s = s;", + " }", + "}") + .doTest(); + } + + @Test + public void noDiagnostic_whenInvokingMethodWithFinalEnumField() { + compilationHelper + .addSourceLines( + "Test.java", + "import com.google.errorprone.annotations.CompileTimeConstant;", + "public enum Test {", + " A(\"A\");", + " @CompileTimeConstant final String s;", + " Test(@CompileTimeConstant String s) {", + " this.s = s;", + " }", + " void invokeCTCMethod() {", + " ctcMethod(s);", + " }", + " void ctcMethod(@CompileTimeConstant String s) {}", + "}") + .doTest(); + } + @Test public void nonConstantField_positive() { compilationHelper