Skip to content

Commit 7e9a100

Browse files
java-team-github-botError Prone Team
authored and
Error Prone Team
committed
Make ThreadSafeTypeParameter useful in the open-source version of ErrorProne.
PiperOrigin-RevId: 667655941
1 parent b4cebef commit 7e9a100

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

annotations/src/main/java/com/google/errorprone/annotations/ThreadSafe.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@
8383
* <li>it is annotated with {@link ThreadSafe}.
8484
* </ul>
8585
*
86-
* <p>This first requirement means the type is at least inherently shallowly thread-safe.
86+
* <p>This first requirement means the type is at least inherently shallowly thread-safe. For types
87+
* with type parameters to be deemed deeply thread-safe, those of these types that denote
88+
* containment must also be deemed thread-safe. A full explanation of this can be found in the
89+
* {@link ThreadSafeTypeParameter} javadoc.
8790
*
8891
* <p>Fields annotated with {@code javax.annotation.concurrent.GuardedBy} are likely the meat of a
8992
* mutable thread-safe class: these are things that need to be mutated, but should be done so in a

core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafety.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.errorprone.annotations.CanIgnoreReturnValue;
3636
import com.google.errorprone.annotations.Immutable;
3737
import com.google.errorprone.annotations.ThreadSafe;
38+
import com.google.errorprone.annotations.ThreadSafeTypeParameter;
3839
import com.google.errorprone.bugpatterns.CanBeStaticAnalyzer;
3940
import com.google.errorprone.suppliers.Supplier;
4041
import com.google.errorprone.util.ASTHelpers;
@@ -95,7 +96,8 @@ public static ThreadSafety.Builder threadSafeBuilder(
9596
.setPurpose(Purpose.FOR_THREAD_SAFE_CHECKER)
9697
.knownTypes(wellKnownThreadSafety)
9798
.markerAnnotations(ImmutableSet.of(ThreadSafe.class.getName()))
98-
.acceptedAnnotations(ImmutableSet.of(Immutable.class.getName()));
99+
.acceptedAnnotations(ImmutableSet.of(Immutable.class.getName()))
100+
.typeParameterAnnotation(ImmutableSet.of(ThreadSafeTypeParameter.class.getName()));
99101
return builder;
100102
}
101103

core/src/test/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeCheckerTest.java

+150
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,29 @@ public void rawClass() {
950950
.doTest();
951951
}
952952

953+
@Ignore("b/26797524 - add tests for generic arguments")
954+
@Test
955+
public void threadSafeTypeParam() {
956+
compilationHelper
957+
.addSourceLines(
958+
"X.java",
959+
"import com.google.common.collect.ImmutableList;",
960+
"import com.google.errorprone.annotations.ThreadSafe;",
961+
"public class X {",
962+
" final ImmutableList<@ThreadSafeTypeParameter ?> unknownSafeType;",
963+
" X (ImmutableList<@ThreadSafeTypeParameter ?> unknownSafeType) {",
964+
" this.unknownSafeType = unknownSafeType;",
965+
" }",
966+
"}")
967+
.addSourceLines(
968+
"Test.java",
969+
"import com.google.common.collect.ImmutableList;",
970+
"class Test {",
971+
" final X badX = new X(ImmutableList.of(ImmutableList.<String>of()));",
972+
"}")
973+
.doTest();
974+
}
975+
953976
@Ignore("b/26797524 - add tests for generic arguments")
954977
@Test
955978
public void mutableTypeParam() {
@@ -1001,6 +1024,78 @@ public void knownThreadSafeFlag() {
10011024
.doTest();
10021025
}
10031026

1027+
@Test
1028+
public void threadSafeTypeParameter() {
1029+
compilationHelper
1030+
.addSourceLines(
1031+
"Test.java",
1032+
"import com.google.errorprone.annotations.ThreadSafe;",
1033+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1034+
"@ThreadSafe class Test<@ThreadSafeTypeParameter T> {",
1035+
" final T t = null;",
1036+
"}")
1037+
.doTest();
1038+
}
1039+
1040+
@Test
1041+
public void threadSafeTypeParameterInstantiation() {
1042+
compilationHelper
1043+
.addSourceLines(
1044+
"A.java",
1045+
"import com.google.errorprone.annotations.ThreadSafe;",
1046+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1047+
"@ThreadSafe class A<@ThreadSafeTypeParameter T> {",
1048+
"}")
1049+
.addSourceLines(
1050+
"Test.java",
1051+
"class Test {",
1052+
" A<String> f() {",
1053+
" return new A<>();",
1054+
" }",
1055+
" A<Object> g() {",
1056+
" // BUG: Diagnostic contains: instantiation of 'T' is not "
1057+
+ "thread-safe, 'Object' is not thread-safe",
1058+
" return new A<>();",
1059+
" }",
1060+
"}")
1061+
.doTest();
1062+
}
1063+
1064+
@Test
1065+
public void threadSafeTypeParameterUsage() {
1066+
compilationHelper
1067+
.addSourceLines(
1068+
"Test.java",
1069+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1070+
"class Test {",
1071+
" static <@ThreadSafeTypeParameter T> void f() {}",
1072+
"}")
1073+
.doTest();
1074+
}
1075+
1076+
@Test
1077+
public void threadSafeTypeParameterUsage_interface() {
1078+
compilationHelper
1079+
.addSourceLines(
1080+
"Test.java",
1081+
"import com.google.errorprone.annotations.ThreadSafe;",
1082+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1083+
"@ThreadSafe interface Test<@ThreadSafeTypeParameter T> {",
1084+
"}")
1085+
.doTest();
1086+
}
1087+
1088+
@Test
1089+
public void threadSafeTypeParameterMutableClass() {
1090+
compilationHelper
1091+
.addSourceLines(
1092+
"Test.java",
1093+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1094+
"// BUG: Diagnostic contains: @ThreadSafeTypeParameter is only supported on",
1095+
"class A<@ThreadSafeTypeParameter T> {}")
1096+
.doTest();
1097+
}
1098+
10041099
@Test
10051100
public void annotatedClassType() {
10061101
compilationHelper
@@ -1015,10 +1110,65 @@ public void annotatedClassType() {
10151110
.doTest();
10161111
}
10171112

1113+
@Test
1114+
public void instantiationWithThreadSafeTypeParameter() {
1115+
compilationHelper
1116+
.addSourceLines(
1117+
"Test.java",
1118+
"import com.google.common.collect.ImmutableList;",
1119+
"import com.google.errorprone.annotations.ThreadSafe;",
1120+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1121+
"@ThreadSafe public class Test<@ThreadSafeTypeParameter T> {",
1122+
" final ImmutableList<T> xs = ImmutableList.of();",
1123+
"}")
1124+
.doTest();
1125+
}
1126+
10181127
// Regression test for b/117937500
1128+
@Test
1129+
public void notAllTypeVarsInstantiated() {
1130+
compilationHelper
1131+
.addSourceLines(
1132+
"Test.java",
1133+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1134+
"import java.util.function.Function;",
1135+
"class Test {",
1136+
" public final <A> void f1(A transform) {}",
1137+
" public <B, @ThreadSafeTypeParameter C> C f2(Function<B, C> fn) {",
1138+
" return null;",
1139+
" }",
1140+
" public final <D, E> void f3(Function<D, E> fn) {",
1141+
" // BUG: Diagnostic contains: instantiation of 'C' is not thread-safe",
1142+
" // 'E' is a non-thread-safe type variable",
1143+
" f1(f2(fn));",
1144+
" }",
1145+
"}")
1146+
.doTest();
1147+
}
10191148

10201149
// javac does not instantiate type variables when they are not used for target typing, so we
10211150
// cannot check whether their instantiations are thread-safe.
1151+
@Ignore
1152+
@Test
1153+
public void notAllTypeVarsInstantiated_shouldFail() {
1154+
compilationHelper
1155+
.addSourceLines(
1156+
"Test.java",
1157+
"import com.google.errorprone.annotations.ThreadSafeTypeParameter;",
1158+
"import java.util.function.Function;",
1159+
"class Test {",
1160+
" public final <A> void f1(A transform) {}",
1161+
" public <@ThreadSafeTypeParameter B, C> C f2(Function<B, C> fn) {",
1162+
" return null;",
1163+
" }",
1164+
" public final <D, E> void f3(Function<D, E> fn) {",
1165+
" // BUG: Diagnostic contains: instantiation of 'B' is not thread-safe",
1166+
" // 'D' is a non-thread-safe type variable",
1167+
" f1(f2(fn));",
1168+
" }",
1169+
"}")
1170+
.doTest();
1171+
}
10221172

10231173
@Test
10241174
public void threadSafeUpperBound() {

0 commit comments

Comments
 (0)