Repository: square/anvil
Pull request #67 (2020-08-24 19:52:15)

Throw a compilation error when the contributed binding has a type argument.
This is an example of a (more complicated) error for `ParentInterface<Map<String, List<Pair<String, Int>>>, SomeOtherType>`

>  com.squareup.anvil.compiler.AnvilCompilationException: Back-end (JVM) Internal error: Binding com.squareup.test.ParentInterface contains type parameters(s) <Map<String, List<Pair<String, Int>>>, SomeOtherType>. Type parameters in bindings are not supported. This binding needs to be contributed to a dagger module manually


Patch:
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/BindingModuleGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/BindingModuleGenerator.kt
index b8a942bf..1bd9f10c 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/BindingModuleGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/BindingModuleGenerator.kt
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.resolve.constants.ArrayValue
 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
 import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
+import org.jetbrains.kotlin.types.KotlinType
 import java.io.File
 import java.util.Locale.US
 
@@ -192,6 +193,7 @@ internal class BindingModuleGenerator(
             val boundType = annotation.boundType(module, contributedClass)
 
             checkExtendsBoundType(type = contributedClass, boundType = boundType)
+            checkNotGeneric(type = contributedClass, boundTypeDescriptor = boundType)
 
             val concreteType = contributedClass.fqNameSafe
             val methodName = concreteType
@@ -224,6 +226,33 @@ internal class BindingModuleGenerator(
     }
   }
 
+  private fun checkNotGeneric(
+    type: ClassDescriptor,
+    boundTypeDescriptor: ClassDescriptor
+  ) {
+    if (boundTypeDescriptor.declaredTypeParameters.isNotEmpty()) {
+
+      fun KotlinType.describeTypeParameters(): String = arguments
+          .ifEmpty { return "" }
+          .joinToString(prefix = "<", postfix = ">") { typeArgument ->
+            typeArgument.type.classDescriptorForType().name.asString() +
+                typeArgument.type.describeTypeParameters()
+          }
+
+      val boundType = type.typeConstructor
+          .supertypes
+          .first { it.classDescriptorForType() == boundTypeDescriptor }
+
+      throw AnvilCompilationException(
+          classDescriptor = boundTypeDescriptor,
+          message = "Binding ${boundTypeDescriptor.fqNameSafe} contains type parameters(s)" +
+              " ${boundType.describeTypeParameters()}." +
+              " Type parameters in bindings are not supported. This binding needs" +
+              " to be contributed to a dagger module manually"
+      )
+    }
+  }
+
   private fun findContributedBindingClasses(projectFiles: Collection<KtFile>) {
     contributedBindingClasses += projectFiles
         .filter {


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
index bb16f812..21dd2b8f 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
@@ -339,6 +339,36 @@ class BindingModuleGeneratorTest(
     }
   }
 
+  @Test fun `the contributed binding class must not have a generic type parameter`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import com.squareup.anvil.annotations.ContributesBinding
+        $import
+
+        interface ParentInterface<T, S>
+        
+        class SomeOtherType
+        
+        @ContributesBinding(Any::class, ParentInterface::class)
+        interface ContributingInterface :
+                ParentInterface<Map<String, List<Pair<String, Int>>>, SomeOtherType>
+        
+        $annotation(Any::class)
+        interface ComponentInterface
+    """
+    ) {
+      assertThat(exitCode).isEqualTo(COMPILATION_ERROR)
+
+      assertThat(messages).contains("Source.kt: (6, 11)")
+      assertThat(messages).contains(
+          "Binding com.squareup.test.ParentInterface contains type parameters(s)" +
+              " <Map<String, List<Pair<String, Int>>>, SomeOtherType>"
+      )
+    }
+  }
+
   private val Class<*>.anyDaggerComponent: AnyDaggerComponent
     get() = anyDaggerComponent(annotationClass)
 }