code: private boolean isTypeParameterThreadSafe(
        TypeVariableSymbol symbol, Set<String> containerTypeParameters) {
      if (!recursiveThreadSafeTypeParameter.add(symbol)) {
        return true;
      }
      // TODO(b/77695285): Prevent type variables that are immutable because of an immutable upper
      // bound to be marked thread-safe via containerOf or typeParameterAnnotation.
      try {
        for (Type bound : symbol.getBounds()) {
          if (!isThreadSafeType(true, containerTypeParameters, bound).isPresent()) {
            // A type variable is thread-safe if any upper bound is thread-safe.
            return true;
          }
        }
        return hasThreadSafeTypeParameterAnnotation(symbol);
      } finally {
        recursiveThreadSafeTypeParameter.remove(symbol);
      }
    }
docs: Returns whether a type parameter is thread-safe.
  
  <p>This is true if either the type parameter's declaration is annotated with {@link
  #typeParameterAnnotation} (indicating it can only be instantiated with thread-safe types), or
  the type parameter has a thread-safe upper bound (sub-classes of thread-safe types are also
  thread-safe).
  
  <p>If a type has a recursive bound, we recursively assume that this type satisfies all
  thread-safety constraints. Recursion can only happen with type variables that have recursive
  type bounds. These type variables do not need to be called out in the "containerOf" attribute
  or annotated with {@link #typeParameterAnnotation}.
  
  <p>Recursion does not apply to other kinds of types because all declared types must be
  annotated thread-safe, which means that thread-safety checkers don't need to analyze all
  referenced types recursively.