Repository: square/anvil (kotlin)

Issue:
where clauses not handled correctly
```kotlin
class SKTokenSelectDelegateImpl<SCDMBinder> @Inject constructor(
  private val prefsManagerLazy: Lazy<PrefsManager>,
  private val slackConnectDmViewBinderLazy: Lazy<SCDMBinder>,
  private val tokenResultsUserViewBinderLazy: Lazy<TokenResultsUserViewBinder>,
  @AndroidIncreaseMpdmLimitFeatureFlag private val increaseMpdmLimit: Boolean,
) : SKTokenSelectDelegate where SCDMBinder : SKListCustomViewBinder, SCDMBinder : SKTokenSlackConnectDmBinder {
```

Generates

```kotlin
public class SKTokenSelectDelegateImpl_Factory<SCDMBinder>(
  private val param0: Provider<@JvmSuppressWildcards PrefsManager>,
  private val param1: Provider<@JvmSuppressWildcards SCDMBinder>,
  private val param2: Provider<@JvmSuppressWildcards TokenResultsUserViewBinder>,
  private val param3: Provider<Boolean>
) : Factory<SKTokenSelectDelegateImpl<SCDMBinder>> {
```

the generated factory is missing the bounds on `SCDMBinder`

Pull request description:
fixes #253 
fixes #251

Patch:
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
index 55825c554..ae9fb14ec 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
@@ -8,6 +8,7 @@ import com.squareup.anvil.compiler.injectFqName
 import com.squareup.anvil.compiler.jvmSuppressWildcardsFqName
 import com.squareup.anvil.compiler.publishedApiFqName
 import com.squareup.anvil.compiler.safePackageString
+import com.squareup.kotlinpoet.TypeVariableName
 import org.jetbrains.kotlin.com.intellij.psi.PsiElement
 import org.jetbrains.kotlin.descriptors.ClassDescriptor
 import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
@@ -402,6 +403,43 @@ internal fun ModuleDescriptor.findClassOrTypeAlias(
   return null
 }
 
+internal fun KtClassOrObject.typeVariableNames(
+  module: ModuleDescriptor
+): List<TypeVariableName> {
+  // Any type which is constrained in a `where` clause is also defined as a type parameter.
+  // It's also technically possible to have one constraint in the type parameter spot, like this:
+  // class MyClass<T : Any> where T : Set<*>, T : MutableCollection<*>
+  // Merge both groups of type parameters in order to get the full list of bounds.
+  val boundsByVariableName = typeParameterList
+    ?.parameters
+    ?.filter { it.fqNameOrNull(module) == null }
+    ?.associateTo(mutableMapOf()) { parameter ->
+      val variableName = parameter.nameAsSafeName.asString()
+      val extendsBound = parameter.extendsBound?.requireTypeName(module)
+
+      variableName to mutableListOf(extendsBound)
+    } ?: mutableMapOf()
+
+  typeConstraintList
+    ?.constraints
+    ?.filter { it.fqNameOrNull(module) == null }
+    ?.forEach { constraint ->
+      val variableName = constraint.subjectTypeParameterName
+        ?.getReferencedName()
+        ?: return@forEach
+      val extendsBound = constraint.boundTypeReference?.requireTypeName(module)
+
+      boundsByVariableName
+        .getValue(variableName)
+        .add(extendsBound)
+    }
+
+  return boundsByVariableName
+    .map { (variableName, bounds) ->
+      TypeVariableName(variableName, bounds.filterNotNull())
+    }
+}
+
 internal fun KtClassOrObject.functions(
   includeCompanionObjects: Boolean
 ): List<KtNamedFunction> = classBodies(includeCompanionObjects).flatMap { it.functions }
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedFactoryGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedFactoryGenerator.kt
index 0d4dd3e4c..a00b45e17 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedFactoryGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedFactoryGenerator.kt
@@ -13,9 +13,9 @@ import com.squareup.anvil.compiler.codegen.buildFile
 import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
 import com.squareup.anvil.compiler.codegen.createGeneratedFile
 import com.squareup.anvil.compiler.codegen.dagger.AssistedFactoryGenerator.AssistedParameterKey.Companion.toKeysList
-import com.squareup.anvil.compiler.codegen.fqNameOrNull
 import com.squareup.anvil.compiler.codegen.hasAnnotation
 import com.squareup.anvil.compiler.codegen.requireClassDescriptor
+import com.squareup.anvil.compiler.codegen.typeVariableNames
 import com.squareup.anvil.compiler.generateClassName
 import com.squareup.anvil.compiler.safePackageString
 import com.squareup.kotlinpoet.ClassName
@@ -27,7 +27,6 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeName
 import com.squareup.kotlinpoet.TypeSpec
-import com.squareup.kotlinpoet.TypeVariableName
 import com.squareup.kotlinpoet.asClassName
 import com.squareup.kotlinpoet.jvm.jvmStatic
 import dagger.internal.InstanceFactory
@@ -172,17 +171,7 @@ internal class AssistedFactoryGenerator : PrivateCodeGenerator() {
     val className = "${clazz.generateClassName()}_Impl"
     val implClass = ClassName(packageName, className)
 
-    val typeParameters = clazz.typeParameterList
-      ?.parameters
-      ?.mapNotNull { parameter ->
-        val extendsBound = parameter.extendsBound?.fqNameOrNull(module)?.asClassName(module)
-        if (parameter.fqNameOrNull(module) == null) {
-          TypeVariableName(parameter.nameAsSafeName.asString(), listOfNotNull(extendsBound))
-        } else {
-          null
-        }
-      }
-      ?: emptyList()
+    val typeParameters = clazz.typeVariableNames(module)
 
     fun ClassName.parameterized(): TypeName {
       return if (typeParameters.isEmpty()) this else parameterizedBy(typeParameters)
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
index 0d48c74fd..e1789027a 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
@@ -10,9 +10,9 @@ import com.squareup.anvil.compiler.codegen.asClassName
 import com.squareup.anvil.compiler.codegen.buildFile
 import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
 import com.squareup.anvil.compiler.codegen.createGeneratedFile
-import com.squareup.anvil.compiler.codegen.fqNameOrNull
 import com.squareup.anvil.compiler.codegen.injectConstructor
 import com.squareup.anvil.compiler.codegen.mapToParameter
+import com.squareup.anvil.compiler.codegen.typeVariableNames
 import com.squareup.anvil.compiler.generateClassName
 import com.squareup.anvil.compiler.safePackageString
 import com.squareup.kotlinpoet.ClassName
@@ -22,7 +22,6 @@ import com.squareup.kotlinpoet.KModifier.PRIVATE
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeSpec
-import com.squareup.kotlinpoet.TypeVariableName
 import com.squareup.kotlinpoet.jvm.jvmStatic
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
 import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -73,17 +72,7 @@ internal class AssistedInjectGenerator : PrivateCodeGenerator() {
 
     checkAssistedParametersAreDistinct(clazz, parametersAssisted)
 
-    val typeParameters = clazz.typeParameterList
-      ?.parameters
-      ?.mapNotNull { parameter ->
-        val extendsBound = parameter.extendsBound?.fqNameOrNull(module)?.asClassName(module)
-        if (parameter.fqNameOrNull(module) == null) {
-          TypeVariableName(parameter.nameAsSafeName.asString(), listOfNotNull(extendsBound))
-        } else {
-          null
-        }
-      }
-      ?: emptyList()
+    val typeParameters = clazz.typeVariableNames(module)
 
     val factoryClass = ClassName(packageName, className)
     val factoryClassParameterized =
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
index 8446d0a93..591edbae8 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
@@ -7,10 +7,10 @@ import com.squareup.anvil.compiler.codegen.asClassName
 import com.squareup.anvil.compiler.codegen.buildFile
 import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
 import com.squareup.anvil.compiler.codegen.createGeneratedFile
-import com.squareup.anvil.compiler.codegen.fqNameOrNull
 import com.squareup.anvil.compiler.codegen.hasAnnotation
 import com.squareup.anvil.compiler.codegen.injectConstructor
 import com.squareup.anvil.compiler.codegen.mapToParameter
+import com.squareup.anvil.compiler.codegen.typeVariableNames
 import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
 import com.squareup.anvil.compiler.generateClassName
 import com.squareup.anvil.compiler.injectFqName
@@ -23,7 +23,6 @@ import com.squareup.kotlinpoet.KModifier.PRIVATE
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeSpec
-import com.squareup.kotlinpoet.TypeVariableName
 import com.squareup.kotlinpoet.asClassName
 import com.squareup.kotlinpoet.jvm.jvmStatic
 import dagger.internal.Factory
@@ -81,17 +80,7 @@ internal class InjectConstructorFactoryGenerator : PrivateCodeGenerator() {
     val constructorInjectParameters = parameters.take(constructorSize)
     val memberInjectParameters = parameters.drop(constructorSize)
 
-    val typeParameters = clazz.typeParameterList
-      ?.parameters
-      ?.mapNotNull { parameter ->
-        val extendsBound = parameter.extendsBound?.fqNameOrNull(module)?.asClassName(module)
-        if (parameter.fqNameOrNull(module) == null) {
-          TypeVariableName(parameter.nameAsSafeName.asString(), listOfNotNull(extendsBound))
-        } else {
-          null
-        }
-      }
-      ?: emptyList()
+    val typeParameters = clazz.typeVariableNames(module)
 
     val factoryClass = ClassName(packageName, className)
     val factoryClassParameterized =


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
index 36fb35fd9..d1dd054c0 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
@@ -409,6 +409,106 @@ public final class AssistedServiceFactory_Impl<T extends CharSequence> implement
     }
   }
 
+  @Test fun `an implementation for a factory class with a type parameter bound by a generic`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      import dagger.assisted.Assisted
+      import dagger.assisted.AssistedFactory
+      import dagger.assisted.AssistedInject
+      
+      data class AssistedService<T : List<String>> @AssistedInject constructor(
+        val int: Int,
+        @Assisted val strings: T
+      )
+      
+      @AssistedFactory
+      interface AssistedServiceFactory<T : List<String>> {
+        fun create(strings: T): AssistedService<T>
+      }
+      """
+    ) {
+      val factoryImplClass = assistedServiceFactory.implClass()
+      val generatedFactoryInstance = assistedService.factoryClass().createInstance(Provider { 5 })
+      val factoryImplInstance = factoryImplClass.createInstance(generatedFactoryInstance)
+
+      val staticMethods = factoryImplClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(1)
+
+      val factoryProvider = staticMethods.single { it.name == "create" }
+        .invoke(null, generatedFactoryInstance) as Provider<*>
+      assertThat(factoryProvider.get()::class.java).isEqualTo(factoryImplClass)
+
+      val assistedServiceInstance = factoryImplClass.declaredMethods
+        .filterNot { it.isStatic }
+        .single { it.name == "create" }
+        .invoke(factoryImplInstance, listOf("Hello"))
+
+      assertThat(assistedServiceInstance).isEqualTo(
+        assistedService.createInstance(
+          5,
+          listOf("Hello")
+        )
+      )
+    }
+  }
+
+  @Test fun `an implementation for a factory class with a type parameter bound by a where clause`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      import dagger.assisted.Assisted
+      import dagger.assisted.AssistedFactory
+      import dagger.assisted.AssistedInject
+      
+      data class AssistedService<T> @AssistedInject constructor(
+        val int: Int,
+        @Assisted val stringBuilder: T
+      ) where T : Appendable, T : CharSequence {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is AssistedService<*>) return false
+    
+            if (int != other.int) return false
+            if (stringBuilder.toString() != other.stringBuilder.toString()) return false
+    
+            return true
+        }
+      }
+      
+      @AssistedFactory
+      interface AssistedServiceFactory<T> where T : Appendable, T : CharSequence {
+        fun create(stringBuilder: T): AssistedService<T>
+      }
+      """
+    ) {
+      val factoryImplClass = assistedServiceFactory.implClass()
+      val generatedFactoryInstance = assistedService.factoryClass().createInstance(Provider { 5 })
+      val factoryImplInstance = factoryImplClass.createInstance(generatedFactoryInstance)
+
+      val staticMethods = factoryImplClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(1)
+
+      val factoryProvider = staticMethods.single { it.name == "create" }
+        .invoke(null, generatedFactoryInstance) as Provider<*>
+      assertThat(factoryProvider.get()::class.java).isEqualTo(factoryImplClass)
+
+      val assistedServiceInstance = factoryImplClass.declaredMethods
+        .filterNot { it.isStatic }
+        .single { it.name == "create" }
+        .invoke(factoryImplInstance, StringBuilder("Hello"))
+
+      assertThat(assistedServiceInstance).isEqualTo(
+        assistedService.createInstance(
+          5,
+          StringBuilder("Hello")
+        )
+      )
+    }
+  }
+
   @Test fun `an implementation for an inner factory class is generated`() {
     compile(
       """
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedInjectGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedInjectGeneratorTest.kt
index e5eaf6a07..d6043df0e 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedInjectGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedInjectGeneratorTest.kt
@@ -309,6 +309,70 @@ public final class AssistedService_Factory {
     }
   }
 
+  @Test fun `a factory class is generated with one generic parameter bound by a generic`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      import dagger.assisted.Assisted
+      import dagger.assisted.AssistedInject
+      
+      data class AssistedService<T : List<String>> @AssistedInject constructor(
+        val int: Int,
+        @Assisted val strings: T
+      )
+      """
+    ) {
+      val factoryClass = assistedService.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+        .invoke(null, Provider { 5 })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+        .invoke(null, 5, listOf("Hello"))
+
+      assertThat(factoryInstance.invokeGet(listOf("Hello"))).isEqualTo(newInstance)
+    }
+  }
+
+  @Test fun `a factory class is generated with one generic parameter bound with a where clause`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      import dagger.assisted.Assisted
+      import dagger.assisted.AssistedInject
+      
+      data class AssistedService<T> @AssistedInject constructor(
+        val int: Int,
+        @Assisted val stringBuilder : T
+      ) where T : Appendable, T : CharSequence
+      """
+    ) {
+      val factoryClass = assistedService.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+        .invoke(null, Provider { 5 })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+        .invoke(null, 5, StringBuilder())
+    }
+  }
+
   @Test fun `a factory class is generated with two generic parameters`() {
     compile(
       """
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
index 1424ab574..876225106 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
@@ -1608,6 +1608,290 @@ public final class InjectClass_Factory<T extends CharSequence> implements Factor
     }
   }
 
+  @Test fun `a factory class is generated for a generic class with a where clause`() {
+    /*
+package com.squareup.test
+
+import dagger.`internal`.Factory
+import java.lang.Appendable
+import javax.inject.Provider
+import kotlin.CharSequence
+import kotlin.Suppress
+import kotlin.jvm.JvmStatic
+
+public class InjectClass_Factory<T>(
+  private val param0: Provider<T>
+) : Factory<InjectClass<T>> where T : Appendable, T : CharSequence {
+  public override fun `get`(): InjectClass<T> = newInstance(param0.get())
+
+  public companion object {
+    @JvmStatic
+    public fun <T> create(param0: Provider<T>): InjectClass_Factory<T> where T : Appendable, T :
+        CharSequence = InjectClass_Factory<T>(param0)
+
+    @JvmStatic
+    public fun <T> newInstance(param0: T): InjectClass<T> where T : Appendable, T : CharSequence =
+        InjectClass<T>(param0)
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      import javax.inject.Provider
+      
+      class InjectClass<T> @Inject constructor(
+        private val t: T
+      ) where T : Appendable, T : CharSequence
+      """
+    ) {
+
+      val factoryClass = injectClass.factoryClass()
+
+      val typeParams = factoryClass.typeParameters
+        .associate { param ->
+          param.name to param.bounds.map { it.typeName }
+        }
+
+      assertThat(typeParams)
+        .containsExactly("T", listOf("java.lang.Appendable", "java.lang.CharSequence"))
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+        .invoke(null, Provider { StringBuilder() })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val instance = staticMethods.single { it.name == "newInstance" }
+        .invoke(null, StringBuilder())
+
+      assertThat(instance).isNotNull()
+      assertThat((factoryInstance as Factory<*>).get()).isNotNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for a generic class with two type parameters and a where clause`() {
+    /*
+package com.squareup.test
+
+import dagger.Lazy
+import dagger.`internal`.Factory
+import java.lang.Appendable
+import javax.inject.Provider
+import kotlin.CharSequence
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.Set
+import kotlin.jvm.JvmStatic
+import kotlin.jvm.JvmSuppressWildcards
+
+public class InjectClass_Factory<T, R : Set<String>>(
+  private val param0: Provider<T>,
+  private val param1: Provider<@JvmSuppressWildcards R>
+) : Factory<InjectClass<T, R>> where T : Appendable, T : CharSequence {
+  public override fun `get`(): InjectClass<T, R> = newInstance(param0.get(),
+      dagger.internal.DoubleCheck.lazy(param1))
+
+  public companion object {
+    @JvmStatic
+    public fun <T, R : Set<String>> create(param0: Provider<T>,
+        param1: Provider<@JvmSuppressWildcards R>): InjectClass_Factory<T, R> where T : Appendable,
+        T : CharSequence = InjectClass_Factory<T, R>(param0, param1)
+
+    @JvmStatic
+    public fun <T, R : Set<String>> newInstance(param0: T, param1: Lazy<@JvmSuppressWildcards R>):
+        InjectClass<T, R> where T : Appendable, T : CharSequence = InjectClass<T, R>(param0, param1)
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import dagger.Lazy
+      import javax.inject.Inject
+      import javax.inject.Provider
+      
+      class InjectClass<T, R : Set<String>> @Inject constructor(
+        private val t: T,
+        private val r: Lazy<R>
+      ) where T : Appendable, T : CharSequence
+      """
+    ) {
+
+      val factoryClass = injectClass.factoryClass()
+
+      val typeParams = factoryClass.typeParameters
+        .associate { param ->
+          param.name to param.bounds.map { it.typeName }
+        }
+
+      assertThat(typeParams)
+        .containsExactly(
+          "T", listOf("java.lang.Appendable", "java.lang.CharSequence"),
+          "R", listOf("java.util.Set<? extends java.lang.String>")
+        )
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+        .containsExactly(
+          Provider::class.java,
+          Provider::class.java
+        )
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+        .invoke(null, Provider { StringBuilder() }, Provider { mutableSetOf<String>() })
+
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val instance = staticMethods.single { it.name == "newInstance" }
+        .invoke(null, StringBuilder(), Lazy { mutableSetOf<String>() })
+
+      assertThat(instance).isNotNull()
+      assertThat((factoryInstance as Factory<*>).get()).isNotNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for a generic class with a where clause with a class bound`() {
+    /*
+package com.squareup.test
+
+import com.squareup.test.Other
+import dagger.`internal`.Factory
+import java.lang.Appendable
+import javax.inject.Provider
+import kotlin.Suppress
+import kotlin.jvm.JvmStatic
+
+public class InjectClass_Factory<T>(
+  private val param0: Provider<T>
+) : Factory<InjectClass<T>> where T : Appendable, T : Other {
+  public override fun `get`(): InjectClass<T> = newInstance(param0.get())
+
+  public companion object {
+    @JvmStatic
+    public fun <T> create(param0: Provider<T>): InjectClass_Factory<T> where T : Appendable, T :
+        Other = InjectClass_Factory<T>(param0)
+
+    @JvmStatic
+    public fun <T> newInstance(param0: T): InjectClass<T> where T : Appendable, T : Other =
+        InjectClass<T>(param0)
+  }
+}
+     */
+
+    // KotlinPoet will automatically add a bound of `Any?` if creating a `TypeVariableName` with an
+    // empty list.  So, improperly handling `TypeVariableName` can result in a constraint like:
+    // `where T : Any?, T : Appendable, T : Other`
+    // This won't compile since a type can only have one bound which is a class.
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      import javax.inject.Provider
+      
+      abstract class Other
+      
+      class InjectClass<T> @Inject constructor(
+        private val t: T
+      ) where T : Appendable, T : Other
+      """
+    ) {
+
+      val factoryClass = injectClass.factoryClass()
+
+      val typeParams = factoryClass.typeParameters
+        .associate { param ->
+          param.name to param.bounds.map { it.typeName }
+        }
+
+      assertThat(typeParams)
+        .containsExactly("T", listOf("com.squareup.test.Other", "java.lang.Appendable"))
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+    }
+  }
+
+  @Test fun `a factory class is generated for a type parameter which extends a generic`() {
+    /*
+package com.squareup.test
+
+import dagger.`internal`.Factory
+import javax.inject.Provider
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+public class InjectClass_Factory<T : List<String>>(
+  private val param0: Provider<T>
+) : Factory<InjectClass<T>> {
+  public override fun `get`(): InjectClass<T> = newInstance(param0.get())
+
+  public companion object {
+    @JvmStatic
+    public fun <T : List<String>> create(param0: Provider<T>): InjectClass_Factory<T> =
+        InjectClass_Factory<T>(param0)
+
+    @JvmStatic
+    public fun <T : List<String>> newInstance(param0: T): InjectClass<T> = InjectClass<T>(param0)
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      import javax.inject.Provider
+ 
+      class InjectClass<T : List<String>> @Inject constructor(
+        private val t: T
+      )
+      """
+    ) {
+
+      val factoryClass = injectClass.factoryClass()
+
+      val typeParams = factoryClass.typeParameters
+        .associate { param ->
+          param.name to param.bounds.map { it.typeName }
+        }
+
+      assertThat(typeParams)
+        .containsExactly("T", listOf("java.util.List<? extends java.lang.String>"))
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+        .containsExactly(Provider::class.java)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+        .invoke(null, Provider { listOf<String>() })
+
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val instance = staticMethods.single { it.name == "newInstance" }
+        .invoke(null, listOf<String>())
+
+      assertThat(instance).isNotNull()
+      assertThat((factoryInstance as Factory<*>).get()).isNotNull()
+    }
+  }
+
   @Test fun `a factory class is generated for an inject constructor without a package`() {
     compile(
       """