Repository: square/anvil (kotlin)

Issue:
Support constructor injection for classes with bounded type parameters is unsupported
Looks like:
```
class InjectClass<T: CharSequence> @Inject constructor(val element: T)
```
is not currently supported by Anvil

Pull request:
Fixes #126

Patch:
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
index 7760ba7b9..a47741b07 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
@@ -61,7 +61,7 @@ internal fun ClassDescriptor.asClassName(): ClassName =
           .reversed()
   )
 
-private fun FqName.asClassName(module: ModuleDescriptor): ClassName {
+internal fun FqName.asClassName(module: ModuleDescriptor): ClassName {
   try {
     return ClassName.bestGuess(asString())
   } catch (ignored: IllegalArgumentException) {
@@ -102,7 +102,8 @@ internal fun KtTypeReference.requireTypeName(
       is KtUserType -> {
         val className = fqNameOrNull(module)?.asClassName(module)
             ?: if (isTypeParameter()) {
-              return TypeVariableName(text)
+              val bounds = findExtendsBound().map { it.asClassName(module) }
+              return TypeVariableName(text, bounds)
             } else {
               throw AnvilCompilationException("Couldn't resolve fqName.", element = this)
             }
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 050d0a2ef..55304ba93 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
@@ -370,6 +370,14 @@ fun KtCallableDeclaration.requireTypeReference(): KtTypeReference =
 
 fun KtUserType.isTypeParameter(): Boolean {
   return parents.filterIsInstance<KtClassOrObject>().first().typeParameters.any {
-    it.textMatches(this)
+    val typeParameter = it.text.split(":").first().trim()
+    typeParameter == text
   }
 }
+
+fun KtUserType.findExtendsBound(): List<FqName> {
+  return parents.filterIsInstance<KtClassOrObject>()
+    .first()
+    .typeParameters
+    .mapNotNull { it.fqName }
+}
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 7d2c510bd..7d9bdf433 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
@@ -64,9 +64,13 @@ internal class InjectConstructorFactoryGenerator : PrivateCodeGenerator() {
 
     val typeParameters = clazz.typeParameterList
       ?.parameters
-      ?.mapNotNull {
-        val fqName = it.fqNameOrNull(module)
-        if (fqName == null) TypeVariableName(it.nameAsSafeName.asString()) else null
+      ?.mapNotNull { parameter ->
+        val extendsBound = parameter.extendsBound?.fqNameOrNull(module)?.asClassName(module)
+        if (parameter.fqNameOrNull(module) == null) {
+          TypeVariableName(parameter.nameAsSafeName.asString(), listOfNotNull(extendsBound))
+        } else {
+          null
+        }
       }
       ?: emptyList()
 


Test patch:
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 c5d2bdcc9..eeca7ab8b 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
@@ -1259,6 +1259,50 @@ public final class InjectClass_Factory implements Factory<InjectClass> {
     }
   }
 
+  @Test fun `a factory class is generated for type parameter which extends class`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.inject.Provider;
+
+public final class InjectClass_Factory<T extends CharSequence> implements Factory<InjectClass<T>> {
+    private final Provider<T> elementProvider;
+
+    public InjectClass_Factory(Provider<T> var1) {
+        this.elementProvider = var1;
+    }
+
+    public InjectClass<T> get() {
+        return newInstance((CharSequence)this.elementProvider.get());
+    }
+
+    public static <T extends CharSequence> InjectClass_Factory<T> create(Provider<T> var0) {
+        return new InjectClass_Factory(var0);
+    }
+
+    public static <T extends CharSequence> InjectClass<T> newInstance(T var0) {
+        return new InjectClass(var0);
+    }
+}
+     */
+
+    compile(
+      """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        import javax.inject.Provider
+        
+        class InjectClass<T : CharSequence> @Inject constructor(element: Provider<T>)
+        """
+    ) {
+      val constructor = classLoader.loadClass("com.squareup.test.InjectClass")
+        .factoryClass().declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+    }
+  }
+
   private fun compile(
     source: String,
     block: Result.() -> Unit = { }