diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeAnalysis.java b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeAnalysis.java index 12257a47a45..9a91ca78cf9 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeAnalysis.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeAnalysis.java @@ -63,6 +63,10 @@ boolean hasThreadSafeTypeParameterAnnotation(TypeVariableSymbol sym) { return threadSafety.hasThreadSafeTypeParameterAnnotation(sym); } + boolean hasThreadSafeElementAnnotation(TypeVariableSymbol sym) { + return threadSafety.hasThreadSafeElementAnnotation(sym); + } + Violation checkInstantiation( Collection classTypeParameters, Collection typeArguments) { return threadSafety.checkInstantiation(classTypeParameters, typeArguments); diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeChecker.java b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeChecker.java index fa17b198377..c60a745d6f3 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeChecker.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/threadsafety/ThreadSafeChecker.java @@ -25,6 +25,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import com.google.errorprone.BugPattern; +import com.google.errorprone.ErrorProneFlags; import com.google.errorprone.VisitorState; import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.ThreadSafe; @@ -75,11 +76,12 @@ public class ThreadSafeChecker extends BugChecker MemberReferenceTreeMatcher { private final WellKnownThreadSafety wellKnownThreadSafety; + private final boolean checkElementUsage; - // Used reflectively @Inject - ThreadSafeChecker(WellKnownThreadSafety wellKnownThreadSafety) { + ThreadSafeChecker(WellKnownThreadSafety wellKnownThreadSafety, ErrorProneFlags flags) { this.wellKnownThreadSafety = wellKnownThreadSafety; + this.checkElementUsage = flags.getBoolean("ThreadSafeChecker:CheckElementUsage").orElse(true); } // check instantiations of `@ThreadSafe`s in method references @@ -129,21 +131,26 @@ public Description matchTypeParameter(TypeParameterTree tree, VisitorState state if (sym == null) { return NO_MATCH; } - ThreadSafeAnalysis analysis = new ThreadSafeAnalysis(this, state, wellKnownThreadSafety); - if (!analysis.hasThreadSafeTypeParameterAnnotation((TypeVariableSymbol) sym)) { - return NO_MATCH; - } switch (sym.owner.getKind()) { case METHOD: case CONSTRUCTOR: return NO_MATCH; default: // fall out } - AnnotationInfo info = analysis.getThreadSafeAnnotation(sym.owner, state); - if (info == null) { - return buildDescription(tree) - .setMessage("@ThreadSafe is only supported on threadsafe classes") - .build(); + ThreadSafeAnalysis analysis = new ThreadSafeAnalysis(this, state, wellKnownThreadSafety); + if (analysis.hasThreadSafeTypeParameterAnnotation((TypeVariableSymbol) sym)) { + if (analysis.getThreadSafeAnnotation(sym.owner, state) == null) { + return buildDescription(tree) + .setMessage("@ThreadSafe.TypeParameter is only supported on threadsafe classes") + .build(); + } + } + if (checkElementUsage && analysis.hasThreadSafeElementAnnotation((TypeVariableSymbol) sym)) { + if (analysis.getThreadSafeAnnotation(sym.owner, state) == null) { + return buildDescription(tree) + .setMessage("@ThreadSafe.Element is only supported on threadsafe classes") + .build(); + } } return NO_MATCH; } 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 9e0ea3c0386..0055a3e642e 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 @@ -634,13 +634,22 @@ public Violation visitType(Type type, Void s) { } /** - * Returns true if the given type parameter's declaration is annotated with {@link - * #typeParameterAnnotation} indicated it will only ever be instantiated with thread-safe types. + * Returns whether the given type parameter's declaration is annotated with {@link + * #typeParameterAnnotation} indicating it will only ever be instantiated with thread-safe types. */ public boolean hasThreadSafeTypeParameterAnnotation(TypeVariableSymbol symbol) { - return typeParameterAnnotation != null - && symbol.getAnnotationMirrors().stream() - .anyMatch(t -> typeParameterAnnotation.contains(t.type.tsym.flatName().toString())); + return symbol.getAnnotationMirrors().stream() + .anyMatch(t -> typeParameterAnnotation.contains(t.type.tsym.flatName().toString())); + } + + /** + * Returns whether the given type parameter's declaration is annotated with {@link + * #containerOfAnnotation} indicating its type-safety determines the type-safety of the outer + * class. + */ + public boolean hasThreadSafeElementAnnotation(TypeVariableSymbol symbol) { + return symbol.getAnnotationMirrors().stream() + .anyMatch(t -> containerOfAnnotation.contains(t.type.tsym.flatName().toString())); } /**