This first requirement means the type is at least inherently shallowly thread-safe.
+ *
This first requirement means the type is at least inherently shallowly thread-safe. For types
+ * with type parameters to be deemed deeply thread-safe, those of these types that denote
+ * containment must also be deemed thread-safe. A full explanation of this can be found in the
+ * {@link ThreadSafeTypeParameter} javadoc.
*
*
Fields annotated with {@code javax.annotation.concurrent.GuardedBy} are likely the meat of a
* mutable thread-safe class: these are things that need to be mutated, but should be done so in a
diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafety.java b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafety.java
index 8e1ba318bd5..263bda79707 100644
--- a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafety.java
+++ b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafety.java
@@ -35,6 +35,7 @@
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.ThreadSafe;
+import com.google.errorprone.annotations.ThreadSafeTypeParameter;
import com.google.errorprone.bugpatterns.CanBeStaticAnalyzer;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.util.ASTHelpers;
@@ -95,7 +96,8 @@ public static ThreadSafety.Builder threadSafeBuilder(
.setPurpose(Purpose.FOR_THREAD_SAFE_CHECKER)
.knownTypes(wellKnownThreadSafety)
.markerAnnotations(ImmutableSet.of(ThreadSafe.class.getName()))
- .acceptedAnnotations(ImmutableSet.of(Immutable.class.getName()));
+ .acceptedAnnotations(ImmutableSet.of(Immutable.class.getName()))
+ .typeParameterAnnotation(ImmutableSet.of(ThreadSafeTypeParameter.class.getName()));
return builder;
}
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 f8de59f8724..323a3e9f114 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
@@ -950,6 +950,29 @@ public void rawClass() {
.doTest();
}
+ @Ignore("b/26797524 - add tests for generic arguments")
+ @Test
+ public void threadSafeTypeParam() {
+ compilationHelper
+ .addSourceLines(
+ "X.java",
+ "import com.google.common.collect.ImmutableList;",
+ "import com.google.errorprone.annotations.ThreadSafe;",
+ "public class X {",
+ " final ImmutableList<@ThreadSafeTypeParameter ?> unknownSafeType;",
+ " X (ImmutableList<@ThreadSafeTypeParameter ?> unknownSafeType) {",
+ " this.unknownSafeType = unknownSafeType;",
+ " }",
+ "}")
+ .addSourceLines(
+ "Test.java",
+ "import com.google.common.collect.ImmutableList;",
+ "class Test {",
+ " final X badX = new X(ImmutableList.of(ImmutableList.of()));",
+ "}")
+ .doTest();
+ }
+
@Ignore("b/26797524 - add tests for generic arguments")
@Test
public void mutableTypeParam() {
@@ -1001,6 +1024,78 @@ public void knownThreadSafeFlag() {
.doTest();
}
+ @Test
+ public void threadSafeTypeParameter() {
+ compilationHelper
+ .addSourceLines(
+ "Test.java",
+ "import com.google.errorprone.annotations.ThreadSafe;",
+ "import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
+ "@ThreadSafe class Test<@ThreadSafeTypeParameter T> {",
+ " final T t = null;",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void threadSafeTypeParameterInstantiation() {
+ compilationHelper
+ .addSourceLines(
+ "A.java",
+ "import com.google.errorprone.annotations.ThreadSafe;",
+ "import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
+ "@ThreadSafe class A<@ThreadSafeTypeParameter T> {",
+ "}")
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " A f() {",
+ " return new A<>();",
+ " }",
+ " A