diff --git a/annotation/pom.xml b/annotation/pom.xml
index fbbba7c5524..4ebcf1808f8 100644
--- a/annotation/pom.xml
+++ b/annotation/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1@BugPattern annotation
diff --git a/annotations/pom.xml b/annotations/pom.xml
index 62ebfe88a40..c37c44b4b64 100644
--- a/annotations/pom.xml
+++ b/annotations/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1error-prone annotations
diff --git a/check_api/pom.xml b/check_api/pom.xml
index 1a96ffd6473..64441b63bba 100644
--- a/check_api/pom.xml
+++ b/check_api/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1error-prone check api
diff --git a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
index 5d552288976..1707c6a4b50 100644
--- a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
+++ b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
@@ -2808,6 +2808,31 @@ private static Method getCaseTreeGetExpressionsMethod() {
}
}
+ /**
+ * Returns true if the given {@link CaseTree} is in the form: {@code case ->
+ * }.
+ */
+ public static boolean isRuleKind(CaseTree caseTree) {
+ Enum> kind;
+ try {
+ kind = (Enum>) GET_CASE_KIND_METHOD.invoke(caseTree);
+ } catch (ReflectiveOperationException e) {
+ return false;
+ }
+ return kind.name().equals("RULE");
+ }
+
+ private static final Method GET_CASE_KIND_METHOD = getGetCaseKindMethod();
+
+ @Nullable
+ private static Method getGetCaseKindMethod() {
+ try {
+ return CaseTree.class.getMethod("getCaseKind");
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
/**
* Retrieves a stream containing all case expressions, in order, for a given {@code CaseTree}.
* This method acts as a facade to the {@code CaseTree.getExpressions()} API, falling back to
diff --git a/check_api/src/main/java/com/google/errorprone/util/RuntimeVersion.java b/check_api/src/main/java/com/google/errorprone/util/RuntimeVersion.java
index 14ae3dd37fc..0785489c645 100644
--- a/check_api/src/main/java/com/google/errorprone/util/RuntimeVersion.java
+++ b/check_api/src/main/java/com/google/errorprone/util/RuntimeVersion.java
@@ -79,6 +79,11 @@ public static boolean isAtLeast21() {
return FEATURE >= 21;
}
+ /** Returns true if the current runtime is JDK 22 or newer. */
+ public static boolean isAtLeast22() {
+ return FEATURE >= 22;
+ }
+
/**
* Returns the latest {@code --release} version.
*
diff --git a/core/pom.xml b/core/pom.xml
index db42176e114..a9628309c7b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1error-prone library
diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/SetUnrecognized.java b/core/src/main/java/com/google/errorprone/bugpatterns/SetUnrecognized.java
index afc49c81a3b..168c5c6e458 100644
--- a/core/src/main/java/com/google/errorprone/bugpatterns/SetUnrecognized.java
+++ b/core/src/main/java/com/google/errorprone/bugpatterns/SetUnrecognized.java
@@ -50,6 +50,9 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
}
ExpressionTree arg = tree.getArguments().get(0);
var argSymbol = getSymbol(arg);
+ if (argSymbol == null) {
+ return NO_MATCH;
+ }
if (!argSymbol.getSimpleName().contentEquals("UNRECOGNIZED")) {
return NO_MATCH;
}
diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpression.java b/core/src/main/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpression.java
new file mode 100644
index 00000000000..090b40e0175
--- /dev/null
+++ b/core/src/main/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpression.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Error Prone Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Description.NO_MATCH;
+import static com.google.errorprone.util.ASTHelpers.isRuleKind;
+
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.matchers.Description;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.Tree;
+
+/** A {@link BugChecker}; see the associated {@link BugPattern} annotation for details. */
+@BugPattern(summary = "Prefer -> switches for switch expressions", severity = WARNING)
+public class TraditionalSwitchExpression extends BugChecker implements BugChecker.CaseTreeMatcher {
+
+ @Override
+ public Description matchCase(CaseTree tree, VisitorState state) {
+ if (isRuleKind(tree)) {
+ return NO_MATCH;
+ }
+ Tree parent = state.getPath().getParentPath().getLeaf();
+ if (!parent.getKind().name().equals("SWITCH_EXPRESSION")) {
+ return NO_MATCH;
+ }
+ return describeMatch(parent);
+ }
+}
diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java
index 71e12a7f237..0e8f41a500e 100644
--- a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java
+++ b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java
@@ -198,6 +198,7 @@ public final class UnusedVariable extends BugChecker implements CompilationUnitT
this.exemptNames =
ImmutableSet.builder()
.add("ignored")
+ .add("") // `var _ = ...` is handled as an empty variable name
.addAll(flags.getListOrEmpty("Unused:exemptNames"))
.build();
diff --git a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java
index 237fdeac8ac..ccd20eae719 100644
--- a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java
+++ b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java
@@ -379,6 +379,7 @@
import com.google.errorprone.bugpatterns.ThrowsUncheckedException;
import com.google.errorprone.bugpatterns.ToStringReturnsNull;
import com.google.errorprone.bugpatterns.TooManyParameters;
+import com.google.errorprone.bugpatterns.TraditionalSwitchExpression;
import com.google.errorprone.bugpatterns.TransientMisuse;
import com.google.errorprone.bugpatterns.TreeToString;
import com.google.errorprone.bugpatterns.TruthAssertExpected;
@@ -1075,6 +1076,7 @@ public static ScannerSupplier warningChecks() {
ThreeLetterTimeZoneID.class,
TimeUnitConversionChecker.class,
ToStringReturnsNull.class,
+ TraditionalSwitchExpression.class,
TruthAssertExpected.class,
TruthConstantAsserts.class,
TruthGetOrDefault.class,
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/SetUnrecognizedTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/SetUnrecognizedTest.java
index 860a4fdb82d..7c55a108c84 100644
--- a/core/src/test/java/com/google/errorprone/bugpatterns/SetUnrecognizedTest.java
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/SetUnrecognizedTest.java
@@ -56,4 +56,19 @@ public void negative() {
"}")
.doTest();
}
+
+ @Test
+ public void negativeNotEnum() {
+ compilationHelper
+ .addSourceLines(
+ "Test.java",
+ "import com.google.errorprone.bugpatterns.proto.Proto3Test.TestProto3Enum;",
+ "import com.google.errorprone.bugpatterns.proto.Proto3Test.TestProto3Message;",
+ "class Test {",
+ " void test() {",
+ " TestProto3Message.newBuilder().setMyString(\"\");",
+ " }",
+ "}")
+ .doTest();
+ }
}
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java
new file mode 100644
index 00000000000..931626e04b4
--- /dev/null
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2024 The Error Prone Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.google.errorprone.CompilationTestHelper;
+import com.google.errorprone.util.RuntimeVersion;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TraditionalSwitchExpressionTest {
+
+ private final CompilationTestHelper testHelper =
+ CompilationTestHelper.newInstance(TraditionalSwitchExpression.class, getClass());
+
+ @Test
+ public void positive() {
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ testHelper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " int f(int i) {",
+ " // BUG: Diagnostic contains: Prefer -> switches for switch expressions",
+ " return switch (i) {",
+ " default:",
+ " yield -1;",
+ " };",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void negativeStatement() {
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ testHelper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " void f(int i) {",
+ " switch (i) {",
+ " default:",
+ " return;",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void negativeArrowStatement() {
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ testHelper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " void f(int i) {",
+ " switch (i) {",
+ " default -> System.err.println();",
+ " }",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void negativeArrow() {
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ testHelper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " int f(int i) {",
+ " return switch (i) {",
+ " default -> -1;",
+ " };",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java
index 92107250de7..4db2f04b54f 100644
--- a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java
@@ -1679,4 +1679,19 @@ public void parameterUsedInOverride() {
.expectUnchanged()
.doTest();
}
+
+ @Test
+ public void underscoreVariable() {
+ assumeTrue(RuntimeVersion.isAtLeast22());
+ refactoringHelper
+ .addInputLines(
+ "Test.java",
+ "class Test {",
+ " public static void main(String[] args) {",
+ " var _ = new Object();",
+ " }",
+ "}")
+ .expectUnchanged()
+ .doTest();
+ }
}
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheckerTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheckerTest.java
index 0c04d598bc2..cd682d46480 100644
--- a/core/src/test/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheckerTest.java
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheckerTest.java
@@ -41,14 +41,7 @@
public class ThreadSafeCheckerTest {
private final CompilationTestHelper compilationHelper =
- CompilationTestHelper.newInstance(ThreadSafeChecker.class, getClass())
- // TODO: b/339025111 - Remove this once ThreadSafeTypeParameter actually exists.
- .addSourceLines(
- "ThreadSafeTypeParameter.java",
- "package com.google.errorprone.annotations;",
- "@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_PARAMETER)",
- "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)",
- "public @interface ThreadSafeTypeParameter {}");
+ CompilationTestHelper.newInstance(ThreadSafeChecker.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringHelper =
BugCheckerRefactoringTestHelper.newInstance(ThreadSafeChecker.class, getClass());
diff --git a/docgen/pom.xml b/docgen/pom.xml
index 8ee00587502..0158d865b2a 100644
--- a/docgen/pom.xml
+++ b/docgen/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1Documentation tool for generating Error Prone bugpattern documentation
diff --git a/docgen_processor/pom.xml b/docgen_processor/pom.xml
index 7d97a4cb4ca..52bfad1f6ae 100644
--- a/docgen_processor/pom.xml
+++ b/docgen_processor/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1JSR-269 annotation processor for @BugPattern annotation
diff --git a/docs/bugpattern/URLEqualsHashCode.md b/docs/bugpattern/URLEqualsHashCode.md
index 1427964c370..aac6cfb9de0 100644
--- a/docs/bugpattern/URLEqualsHashCode.md
+++ b/docs/bugpattern/URLEqualsHashCode.md
@@ -1,2 +1,7 @@
-Equals and HashCode method of java.net.URL make blocking network calls. Either
-use java.net.URI or if that isn't possible, use Collection or List.
+The `equals` and `hashCode` methods of `java.net.URL` make blocking network
+calls. When you place a `URL` into a hash-based container, the container invokes
+those methods.
+
+Prefer `java.net.URI`. Or, if you must use `URL` in a
+collection, prefer to use a non-hash-based container like a `List`, and
+avoid calling methods like `contains` (which calls `equals`) on it.
diff --git a/pom.xml b/pom.xml
index f7cad3c75bd..63fb9ae1494 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
Error Prone parent POMcom.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1pomError Prone is a static analysis tool for Java that catches common programming mistakes at compile-time.
@@ -45,7 +45,7 @@
1.6.133.19.61.43.3
- 0.3.0
+ 1.0.05.1.0
diff --git a/refaster/pom.xml b/refaster/pom.xml
index 968b0c2bae4..9d1910f0cdd 100644
--- a/refaster/pom.xml
+++ b/refaster/pom.xml
@@ -19,7 +19,7 @@
error_prone_parentcom.google.errorprone
- 1.0-HEAD-SNAPSHOT
+ 2.29.14.0.0
diff --git a/test_helpers/pom.xml b/test_helpers/pom.xml
index 76fbfaf30d9..fd802cc3708 100644
--- a/test_helpers/pom.xml
+++ b/test_helpers/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1error-prone test helpers
diff --git a/type_annotations/pom.xml b/type_annotations/pom.xml
index 365123af188..ebcd322a8d9 100644
--- a/type_annotations/pom.xml
+++ b/type_annotations/pom.xml
@@ -21,7 +21,7 @@
com.google.errorproneerror_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.1error-prone type annotations
diff --git a/type_annotations/src/main/java/com/google/errorprone/annotations/ThreadSafeTypeParameter.java b/type_annotations/src/main/java/com/google/errorprone/annotations/ThreadSafeTypeParameter.java
new file mode 100644
index 00000000000..2f3dbf4c4b4
--- /dev/null
+++ b/type_annotations/src/main/java/com/google/errorprone/annotations/ThreadSafeTypeParameter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 The Error Prone Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.errorprone.annotations;
+
+import static java.lang.annotation.ElementType.TYPE_PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * When a {@link ThreadSafe} class has type parameters, annotating a parameter with {@code
+ * ThreadSafeTypeParameter} enforces that declarations of this class must, for that type parameter,
+ * use a type that is itself thread-safe.
+ *
+ *
Additionally, only type parameters that are annotated with {@code ThreadSafeTypeParameter} can
+ * be used as field types that are not {@link
+ * com.google.errorprone.annotations.concurrent.GuardedBy @GuardedBy}.
+ *
+ *
In more detail, consider this (valid) class:
+ *
+ *
+ *
+ * Each of these three type parameters is valid for a different reason: type parameter {@code A} is
+ * ok because it is simply not used as the type of a field; type parameter {@code B} is ok because
+ * it is used as the type of a field that is declared to be {@code @GuardedBy}; finally, type
+ * parameter {@code C} is ok because it is annotated with {@code ThreadSafeTypeParameter}.
+ * Furthermore, the declaration {@code MyThreadSafeClass