Repository: square/anvil
Pull request #66 (2020-08-24 18:34:18)

Generate Dagger Factories
The code that Dagger generates for modules, field injection and inject constructors is trivial. The real magic happens when Dagger compiles components or subcomponents.

This PR gives us the option to use Anvil instead of the Dagger annotation processor to generate the factories that Dagger would generate. This improves build times for many modules. 

These numbers are from our code base when building one of the tiniest development apps:
  | Stub generation | Kapt | Javac | Kotlinc | Sum
--- | --- | --- | --- | --- | ---
With Dagger | 12.976 | 40.377 | 8.571 | 10.241 | 72.165
Anvil | 0 | 0 | 6.965 | 17.748 | 24.713

Notice that many tasks don't exist anymore with this optimization. Since no annotation processor needs to run Kapt stub generation and Kapt itself are gone. Anvil generates Kotlin code instead of Java unlike the Dagger annotation processor. In many modules javac is a no-op now, because there aren't any Java sources anymore. Koltinc on the other side is slower due to applying the Kotlin compiler plugin itself and that's where the work happens.

In CI for SPOS I've measured 9:37 with Dagger vs 7:57 with Anvil.

This optimization can only be enabled in modules that don't compile a Dagger component. If Anvil sees a component or subcomponent, then it throws a compilation error. This new feature can be enabled through a new Gradle extension:
```groovy
anvil {
  generateDaggerFactories = true // default is 'false'
}
```


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 00591064..ff61d74e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,11 +38,11 @@ jobs:
       # wouldn't need to duplicate the next step and depending on the OS enable / disable them.
       - name: ${{ matrix.gradle-task }} with Gradle Ubuntu
         run: ./gradlew ${{ matrix.gradle-task }} --no-build-cache --no-daemon --stacktrace -Psquare.kotlinVersion=${{ matrix.kotlin-version }}
-        if: ${{ startsWith(matrix.os, 'ubuntu') }}
+        if: ${{ startsWith(matrix.os, 'ubuntu') && (matrix.gradle-task != 'test' || matrix.kotlin-version == '1.3.72') }}
 
       - name: ${{ matrix.gradle-task }} with Gradle Windows
         run: ./gradlew.bat ${{ matrix.gradle-task }} --no-build-cache --no-daemon --stacktrace "-Psquare.kotlinVersion=${{ matrix.kotlin-version }}"
-        if: ${{ startsWith(matrix.os, 'windows') }}
+        if: ${{ startsWith(matrix.os, 'windows')  && (matrix.gradle-task != 'test' || matrix.kotlin-version == '1.3.72') }}
 
       - name: Upload Lint Results
         uses: actions/upload-artifact@v2
diff --git a/compiler/build.gradle b/compiler/build.gradle
index 7d964c92..e0ab4a31 100644
--- a/compiler/build.gradle
+++ b/compiler/build.gradle
@@ -7,12 +7,14 @@ dependencies {
   implementation deps.dagger2.dagger
   implementation deps.jsr250
   implementation deps.kotlin.stdlib
+  implementation deps.kotlinpoet
 
   compileOnly deps.auto.service.annotations
   compileOnly deps.kotlin.compiler
 
   kapt deps.auto.service.processor
 
+  testImplementation deps.dagger2.compiler
   testImplementation deps.kotlin.compile_testing
   testImplementation deps.kotlin.compiler
   testImplementation deps.truth
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/AnvilCommandLineProcessor.kt b/compiler/src/main/java/com/squareup/anvil/compiler/AnvilCommandLineProcessor.kt
index a6911234..db6af28e 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/AnvilCommandLineProcessor.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/AnvilCommandLineProcessor.kt
@@ -10,6 +10,10 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
 internal const val srcGenDirName = "src-gen-dir"
 internal val srcGenDirKey = CompilerConfigurationKey.create<String>("anvil $srcGenDirName")
 
+internal const val generateDaggerFactoriesName = "generate-dagger-factories"
+internal val generateDaggerFactoriesKey =
+  CompilerConfigurationKey.create<Boolean>("anvil $generateDaggerFactoriesName")
+
 /**
  * Parses arguments from the Gradle plugin for the compiler plugin.
  */
@@ -24,6 +28,15 @@ class AnvilCommandLineProcessor : CommandLineProcessor {
           description = "Path to directory in which Anvil specific code should be generated",
           required = true,
           allowMultipleOccurrences = false
+      ),
+      CliOption(
+          optionName = generateDaggerFactoriesName,
+          valueDescription = "<true|false>",
+          description = "Whether Anvil should generate Factory classes that the Dagger " +
+              "annotation processor would generate for @Provides methods and @Inject " +
+              "constructors.",
+          required = false,
+          allowMultipleOccurrences = false
       )
   )
 
@@ -34,6 +47,8 @@ class AnvilCommandLineProcessor : CommandLineProcessor {
   ) {
     when (option.optionName) {
       srcGenDirName -> configuration.put(srcGenDirKey, value)
+      generateDaggerFactoriesName ->
+        configuration.put(generateDaggerFactoriesKey, value.toBoolean())
     }
   }
 }
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/AnvilComponentRegistrar.kt b/compiler/src/main/java/com/squareup/anvil/compiler/AnvilComponentRegistrar.kt
index ed35c1e2..d18f23e0 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/AnvilComponentRegistrar.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/AnvilComponentRegistrar.kt
@@ -5,6 +5,10 @@ import com.squareup.anvil.compiler.codegen.BindingModuleGenerator
 import com.squareup.anvil.compiler.codegen.CodeGenerationExtension
 import com.squareup.anvil.compiler.codegen.ContributesBindingGenerator
 import com.squareup.anvil.compiler.codegen.ContributesToGenerator
+import com.squareup.anvil.compiler.codegen.dagger.ComponentDetectorCheck
+import com.squareup.anvil.compiler.codegen.dagger.InjectConstructorFactoryGenerator
+import com.squareup.anvil.compiler.codegen.dagger.MembersInjectorGenerator
+import com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator
 import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
 import org.jetbrains.kotlin.com.intellij.mock.MockProject
 import org.jetbrains.kotlin.com.intellij.openapi.extensions.Extensions
@@ -28,13 +32,22 @@ class AnvilComponentRegistrar : ComponentRegistrar {
     val sourceGenFolder = File(configuration.getNotNull(srcGenDirKey))
     val scanner = ClassScanner()
 
+    val codeGenerators = mutableListOf(
+        ContributesToGenerator(),
+        ContributesBindingGenerator(),
+        BindingModuleGenerator(scanner)
+    )
+
+    if (configuration.get(generateDaggerFactoriesKey, false)) {
+      codeGenerators += ProvidesMethodFactoryGenerator()
+      codeGenerators += InjectConstructorFactoryGenerator()
+      codeGenerators += MembersInjectorGenerator()
+      codeGenerators += ComponentDetectorCheck()
+    }
+
     val codeGenerationExtension = CodeGenerationExtension(
         codeGenDir = sourceGenFolder,
-        codeGenerators = listOf(
-            ContributesToGenerator(),
-            ContributesBindingGenerator(),
-            BindingModuleGenerator(scanner)
-        )
+        codeGenerators = codeGenerators
     )
 
     // It's important to register our extension at the first position. The compiler calls each
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
index f485a4eb..a7b89e07 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
@@ -6,10 +6,14 @@ import com.squareup.anvil.annotations.MergeComponent
 import com.squareup.anvil.annotations.MergeSubcomponent
 import com.squareup.anvil.annotations.compat.MergeInterfaces
 import com.squareup.anvil.annotations.compat.MergeModules
+import com.squareup.anvil.compiler.codegen.requireFqName
 import dagger.Binds
 import dagger.Component
+import dagger.Lazy
 import dagger.Module
+import dagger.Provides
 import dagger.Subcomponent
+import dagger.internal.DoubleCheck
 import org.jetbrains.kotlin.codegen.asmType
 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
 import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -19,6 +23,8 @@ import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.platform.has
 import org.jetbrains.kotlin.platform.jvm.JvmPlatform
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.resolve.DescriptorUtils
 import org.jetbrains.kotlin.resolve.constants.ArrayValue
 import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -29,6 +35,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
 import org.jetbrains.kotlin.resolve.descriptorUtil.module
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.org.objectweb.asm.Type
+import javax.inject.Inject
+import javax.inject.Provider
 
 internal val mergeComponentFqName = FqName(MergeComponent::class.java.canonicalName)
 internal val mergeSubcomponentFqName = FqName(MergeSubcomponent::class.java.canonicalName)
@@ -40,6 +48,13 @@ internal val daggerComponentFqName = FqName(Component::class.java.canonicalName)
 internal val daggerSubcomponentFqName = FqName(Subcomponent::class.java.canonicalName)
 internal val daggerModuleFqName = FqName(Module::class.java.canonicalName)
 internal val daggerBindsFqName = FqName(Binds::class.java.canonicalName)
+internal val daggerProvidesFqName = FqName(Provides::class.java.canonicalName)
+internal val daggerLazyFqName = FqName(Lazy::class.java.canonicalName)
+internal val injectFqName = FqName(Inject::class.java.canonicalName)
+internal val providerFqName = FqName(Provider::class.java.canonicalName)
+internal val jvmSuppressWildcardsFqName = FqName(JvmSuppressWildcards::class.java.canonicalName)
+
+internal val daggerDoubleCheckFqNameString = DoubleCheck::class.java.canonicalName
 
 internal const val HINT_CONTRIBUTES_PACKAGE_PREFIX = "anvil.hint.merge"
 internal const val HINT_BINDING_PACKAGE_PREFIX = "anvil.hint.binding"
@@ -149,3 +164,16 @@ internal fun AnnotationDescriptor.boundType(
               "@${contributesBindingFqName.shortName()} annotation."
       )
 }
+
+internal fun KtClassOrObject.generateClassName(
+  separator: String = "_"
+): String =
+  parentsWithSelf
+      .filterIsInstance<KtClassOrObject>()
+      .toList()
+      .reversed()
+      .joinToString(separator = separator) {
+        it.requireFqName()
+            .shortName()
+            .asString()
+      }
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 1bd9f10c..2c394581 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
@@ -16,6 +16,7 @@ import com.squareup.anvil.compiler.contributesBindingFqName
 import com.squareup.anvil.compiler.contributesToFqName
 import com.squareup.anvil.compiler.daggerBindsFqName
 import com.squareup.anvil.compiler.daggerModuleFqName
+import com.squareup.anvil.compiler.generateClassName
 import com.squareup.anvil.compiler.getAnnotationValue
 import com.squareup.anvil.compiler.mergeComponentFqName
 import com.squareup.anvil.compiler.mergeModulesFqName
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.psi.KtProperty
-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
@@ -108,7 +108,7 @@ internal class BindingModuleGenerator(
               ?.let { excludedTypesForScope[scope] = it }
 
           val packageName = generatePackageName(psiClass)
-          val className = generateClassName(psiClass)
+          val className = psiClass.generateClassName()
 
           val directory = File(codeGenDir, packageName.replace('.', File.separatorChar))
           val file = File(directory, "$className.kt")
@@ -294,16 +294,8 @@ internal class BindingModuleGenerator(
     }
   }
 
-  private fun generateClassName(psiClass: KtClassOrObject): String =
-    psiClass.parentsWithSelf
-        .filterIsInstance<KtClassOrObject>()
-        .toList()
-        .reversed()
-        .joinToString(separator = "", postfix = ANVIL_MODULE_SUFFIX) {
-          it.requireFqName()
-              .shortName()
-              .asString()
-        }
+  private fun KtClassOrObject.generateClassName(): String =
+    generateClassName(separator = "") + ANVIL_MODULE_SUFFIX
 
   private fun generatePackageName(psiClass: KtClassOrObject): String =
     "$MODULE_PACKAGE_PREFIX.${psiClass.containingKtFile.packageFqName}"
@@ -314,7 +306,7 @@ internal class BindingModuleGenerator(
     bindingMethods: List<String>
   ): String {
     val packageName = generatePackageName(psiClass)
-    val className = generateClassName(psiClass)
+    val className = psiClass.generateClassName()
 
     return """
       package $packageName
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 fba4f1a3..8fef39b8 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
@@ -1,21 +1,34 @@
 package com.squareup.anvil.compiler.codegen
 
 import com.squareup.anvil.compiler.AnvilCompilationException
+import com.squareup.anvil.compiler.jvmSuppressWildcardsFqName
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
 import org.jetbrains.kotlin.descriptors.resolveClassByFqName
 import org.jetbrains.kotlin.incremental.components.NoLookupLocation.FROM_BACKEND
 import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtAnnotated
 import org.jetbrains.kotlin.psi.KtAnnotationEntry
 import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtClassBody
 import org.jetbrains.kotlin.psi.KtClassLiteralExpression
 import org.jetbrains.kotlin.psi.KtClassOrObject
 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.psi.KtNameReferenceExpression
+import org.jetbrains.kotlin.psi.KtNamedDeclaration
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.KtNullableType
+import org.jetbrains.kotlin.psi.KtTypeArgumentList
+import org.jetbrains.kotlin.psi.KtTypeReference
+import org.jetbrains.kotlin.psi.KtUserType
 import org.jetbrains.kotlin.psi.KtValueArgument
 import org.jetbrains.kotlin.psi.KtValueArgumentName
+import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
 
+private val kotlinAnnotations = listOf(jvmSuppressWildcardsFqName)
+
 internal fun KtFile.classesAndInnerClasses(): Sequence<KtClassOrObject> {
   val children = findChildrenByClass(KtClassOrObject::class.java)
 
@@ -28,21 +41,32 @@ internal fun KtFile.classesAndInnerClasses(): Sequence<KtClassOrObject> {
   }.flatMap { it.asSequence() }
 }
 
-internal fun KtClassOrObject.requireFqName(): FqName = requireNotNull(fqName) {
+internal fun KtNamedDeclaration.requireFqName(): FqName = requireNotNull(fqName) {
   "fqName was null for $this, $nameAsSafeName"
 }
 
-internal fun KtClassOrObject.isInterface(): Boolean = this is KtClass && this.isInterface()
+internal fun KtAnnotated.isInterface(): Boolean = this is KtClass && this.isInterface()
 
-internal fun KtClassOrObject.hasAnnotation(fqName: FqName): Boolean {
+internal fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean {
   return findAnnotation(fqName) != null
 }
 
-internal fun KtClassOrObject.findAnnotation(fqName: FqName): KtAnnotationEntry? {
+internal fun KtAnnotated.findAnnotation(fqName: FqName): KtAnnotationEntry? {
   val annotationEntries = annotationEntries
   if (annotationEntries.isEmpty()) return null
 
-  // Check first if the fully qualified name is used, e.g. `@dagger.Module`.
+  // Look first if it's a Kotlin annotation. These annotations are usually not imported and the
+  // remaining checks would fail.
+  annotationEntries.firstOrNull { annotation ->
+    kotlinAnnotations
+        .any { kotlinAnnotationFqName ->
+          val text = annotation.text
+          text.startsWith("@${kotlinAnnotationFqName.shortName()}") ||
+              text.startsWith("@$kotlinAnnotationFqName")
+        }
+  }?.let { return it }
+
+  // Check if the fully qualified name is used, e.g. `@dagger.Module`.
   val annotationEntry = annotationEntries.firstOrNull {
     it.text.startsWith("@${fqName.asString()}")
   }
@@ -55,21 +79,28 @@ internal fun KtClassOrObject.findAnnotation(fqName: FqName): KtAnnotationEntry?
       }
       ?: return null
 
+  val importPaths = containingKtFile.importDirectives.mapNotNull { it.importPath }
+
   // If the simple name is used, check that the annotation is imported.
-  val hasImport = containingKtFile.importDirectives
-      .mapNotNull { it.importPath }
+  val hasImport = importPaths.any { it.fqName == fqName }
+  if (hasImport) return annotationEntryShort
+
+  // Look for star imports and make a guess.
+  val hasStarImport = importPaths
+      .filter { it.isAllUnder }
       .any {
-        it.fqName == fqName
+        fqName.asString().startsWith(it.fqName.asString())
       }
+  if (hasStarImport) return annotationEntryShort
 
-  return if (hasImport) annotationEntryShort else null
+  return null
 }
 
 internal fun KtClassOrObject.scope(
   annotationFqName: FqName,
   module: ModuleDescriptor
 ): FqName {
-  val scopeElement = findScopeClassLiteralExpression(annotationFqName)
+  return findScopeClassLiteralExpression(annotationFqName)
       .let {
         val children = it.children
         children.singleOrNull() ?: throw AnvilCompilationException(
@@ -77,44 +108,7 @@ internal fun KtClassOrObject.scope(
             element = this
         )
       }
-
-  val scopeClassReference = when (scopeElement) {
-    // If a fully qualified name is used, then we're done and don't need to do anything further.
-    is KtDotQualifiedExpression -> return FqName(scopeElement.text)
-    is KtNameReferenceExpression -> scopeElement.getReferencedName()
-    else -> throw AnvilCompilationException(
-        "Don't know how to handle Psi element: ${scopeElement.text}",
-        element = this
-    )
-  }
-
-  // First look in the imports for the reference name. If the class is imported, then we know the
-  // fully qualified name.
-  containingKtFile.importDirectives
-      .mapNotNull { it.importPath }
-      .firstOrNull {
-        it.fqName.shortName()
-            .asString() == scopeClassReference
-      }
-      ?.let { return it.fqName }
-
-  // If there is no import, then try to resolve the class with the same package as this file.
-  module
-      .resolveClassByFqName(
-          FqName("${containingKtFile.packageFqName}.$scopeClassReference"),
-          FROM_BACKEND
-      )
-      ?.let { return it.fqNameSafe }
-
-  // If this doesn't work, then maybe a class from the Kotlin package is used.
-  module.resolveClassByFqName(FqName("kotlin.$scopeClassReference"), FROM_BACKEND)
-      ?.let { return it.fqNameSafe }
-
-  // Everything else isn't supported.
-  throw AnvilCompilationException(
-      "Couldn't resolve scope $scopeClassReference for Psi element: $text",
-      element = this
-  )
+      .requireFqName(module)
 }
 
 private fun KtClassOrObject.findScopeClassLiteralExpression(
@@ -159,3 +153,94 @@ private fun KtClassOrObject.findScopeClassLiteralExpression(
           element = this
       )
 }
+
+internal fun PsiElement.fqNameOrNull(
+  module: ModuleDescriptor
+): FqName? {
+  // Usually it's the opposite way, the require*() method calls the nullable method. But in this
+  // case we'd like to preserve the better error messages in case something goes wrong.
+  return try {
+    requireFqName(module)
+  } catch (e: AnvilCompilationException) {
+    null
+  }
+}
+
+internal fun PsiElement.requireFqName(
+  module: ModuleDescriptor
+): FqName {
+  val containingKtFile = parentsWithSelf
+      .filterIsInstance<KtClassOrObject>()
+      .first()
+      .containingKtFile
+
+  fun failTypeHandling(): Nothing = throw AnvilCompilationException(
+      "Don't know how to handle Psi element: $text",
+      element = this
+  )
+
+  val classReference = when (this) {
+    // If a fully qualified name is used, then we're done and don't need to do anything further.
+    is KtDotQualifiedExpression -> return FqName(text)
+    is KtNameReferenceExpression -> getReferencedName()
+    is KtUserType -> referencedName ?: failTypeHandling()
+    is KtTypeReference -> text
+    is KtNullableType -> return innerType?.requireFqName(module) ?: failTypeHandling()
+    else -> failTypeHandling()
+  }
+
+  // First look in the imports for the reference name. If the class is imported, then we know the
+  // fully qualified name.
+  containingKtFile.importDirectives
+      .mapNotNull { it.importPath }
+      .firstOrNull {
+        it.fqName.shortName()
+            .asString() == classReference
+      }
+      ?.let { return it.fqName }
+
+  // If there is no import, then try to resolve the class with the same package as this file.
+  module
+      .resolveClassByFqName(
+          FqName("${containingKtFile.packageFqName}.$classReference"),
+          FROM_BACKEND
+      )
+      ?.let { return it.fqNameSafe }
+
+  // If this doesn't work, then maybe a class from the Kotlin package is used.
+  module.resolveClassByFqName(FqName("kotlin.$classReference"), FROM_BACKEND)
+      ?.let { return it.fqNameSafe }
+
+  // If this doesn't work, then maybe a class from the Kotlin package is used.
+  module.resolveClassByFqName(FqName("kotlin.collections.$classReference"), FROM_BACKEND)
+      ?.let { return it.fqNameSafe }
+
+  // Everything else isn't supported.
+  throw AnvilCompilationException(
+      "Couldn't resolve FqName $classReference for Psi element: $text",
+      element = this
+  )
+}
+
+internal fun KtClassOrObject.functions(
+  includeCompanionObjects: Boolean
+): List<KtNamedFunction> {
+  val elements = children.toMutableList()
+  if (includeCompanionObjects) {
+    elements += companionObjects.flatMap { it.children.toList() }
+  }
+
+  return elements
+      .filterIsInstance<KtClassBody>()
+      .flatMap { it.functions }
+}
+
+fun KtTypeReference.isNullable(): Boolean = typeElement is KtNullableType
+
+fun KtTypeReference.isGenericType(): Boolean {
+  val typeElement = typeElement ?: return false
+  val children = typeElement.children
+
+  if (children.size != 2) return false
+  return children[1] is KtTypeArgumentList
+}
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ComponentDetectorCheck.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ComponentDetectorCheck.kt
new file mode 100644
index 00000000..372e5a37
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ComponentDetectorCheck.kt
@@ -0,0 +1,41 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.AnvilCompilationException
+import com.squareup.anvil.compiler.codegen.CodeGenerator
+import com.squareup.anvil.compiler.codegen.CodeGenerator.GeneratedFile
+import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
+import com.squareup.anvil.compiler.codegen.hasAnnotation
+import com.squareup.anvil.compiler.daggerComponentFqName
+import com.squareup.anvil.compiler.daggerSubcomponentFqName
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+
+internal class ComponentDetectorCheck : CodeGenerator {
+  override fun generateCode(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    projectFiles: Collection<KtFile>
+  ): Collection<GeneratedFile> {
+    val component = projectFiles
+        .asSequence()
+        .flatMap { it.classesAndInnerClasses() }
+        .firstOrNull() { clazz ->
+          clazz.hasAnnotation(daggerComponentFqName) || clazz.hasAnnotation(
+              daggerSubcomponentFqName
+          )
+        }
+
+    if (component != null) {
+      throw AnvilCompilationException(
+          message = "Anvil cannot generate the code for Dagger components or subcomponents. In " +
+              "these cases the Dagger annotation processor is required. Enabling the Dagger " +
+              "annotation processor and turning on Anvil to generate Dagger factories is " +
+              "redundant. Set 'generateDaggerFactories' to false.",
+          element = component
+      )
+    }
+
+    return emptyList()
+  }
+}
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
new file mode 100644
index 00000000..2b842494
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
@@ -0,0 +1,175 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.codegen.CodeGenerator
+import com.squareup.anvil.compiler.codegen.CodeGenerator.GeneratedFile
+import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
+import com.squareup.anvil.compiler.codegen.hasAnnotation
+import com.squareup.anvil.compiler.generateClassName
+import com.squareup.anvil.compiler.injectFqName
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier.OVERRIDE
+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.asClassName
+import com.squareup.kotlinpoet.jvm.jvmStatic
+import dagger.internal.Factory
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtConstructor
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.allConstructors
+import java.io.File
+
+internal class InjectConstructorFactoryGenerator : CodeGenerator {
+  override fun generateCode(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    projectFiles: Collection<KtFile>
+  ): Collection<GeneratedFile> {
+    return projectFiles
+        .asSequence()
+        .flatMap { it.classesAndInnerClasses() }
+        .mapNotNull { clazz ->
+          clazz.allConstructors
+              .singleOrNull { it.hasAnnotation(injectFqName) }
+              ?.let {
+                generateFactoryClass(codeGenDir, module, clazz, it)
+              }
+        }
+        .toList()
+  }
+
+  @OptIn(ExperimentalStdlibApi::class)
+  private fun generateFactoryClass(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    clazz: KtClassOrObject,
+    constructor: KtConstructor<*>
+  ): GeneratedFile {
+    val packageName = clazz.containingKtFile.packageFqName.asString()
+    val className = "${clazz.generateClassName()}_Factory"
+    val classType = clazz.asClassName()
+
+    val parameters = constructor.getValueParameters().mapToParameter(module)
+
+    val factoryClass = ClassName(packageName, className)
+
+    val content = FileSpec.builder(packageName, className)
+        .addType(
+            TypeSpec
+                .classBuilder(factoryClass)
+                .addAnvilAnnotation()
+                .addSuperinterface(Factory::class.asClassName().parameterizedBy(classType))
+                .apply {
+                  if (parameters.isNotEmpty()) {
+                    primaryConstructor(
+                        FunSpec.constructorBuilder()
+                            .apply {
+                              parameters.forEach { parameter ->
+                                addParameter(parameter.name, parameter.providerTypeName)
+                              }
+                            }
+                            .build()
+                    )
+
+                    parameters.forEach { parameter ->
+                      addProperty(
+                          PropertySpec.builder(parameter.name, parameter.providerTypeName)
+                              .initializer(parameter.name)
+                              .addModifiers(PRIVATE)
+                              .build()
+                      )
+                    }
+                  }
+                }
+                .addFunction(
+                    FunSpec.builder("get")
+                        .addModifiers(OVERRIDE)
+                        .returns(classType)
+                        .apply {
+                          val argumentList = parameters.asArgumentList(
+                              asProvider = true,
+                              includeModule = false
+                          )
+
+                          addStatement("return newInstance($argumentList)")
+                        }
+                        .build()
+                )
+                .addType(
+                    TypeSpec
+                        .companionObjectBuilder()
+                        .apply {
+                          if (parameters.isEmpty()) {
+                            addProperty(
+                                PropertySpec.builder("instance", factoryClass)
+                                    .addModifiers(PRIVATE)
+                                    .initializer("%T()", factoryClass)
+                                    .build()
+                            )
+                          }
+                        }
+                        .addFunction(
+                            FunSpec.builder("create")
+                                .jvmStatic()
+                                .apply {
+                                  if (parameters.isEmpty()) {
+                                    addStatement("return instance")
+                                  } else {
+                                    parameters.forEach { parameter ->
+                                      addParameter(parameter.name, parameter.providerTypeName)
+                                    }
+
+                                    val argumentList = parameters.asArgumentList(
+                                        asProvider = false,
+                                        includeModule = false
+                                    )
+
+                                    addStatement(
+                                        "return %T($argumentList)",
+                                        factoryClass
+                                    )
+                                  }
+                                }
+                                .returns(factoryClass)
+                                .build()
+                        )
+                        .addFunction(
+                            FunSpec.builder("newInstance")
+                                .jvmStatic()
+                                .apply {
+                                  parameters.forEach { parameter ->
+                                    addParameter(
+                                        name = parameter.name,
+                                        type = parameter.originalTypeName
+                                    )
+                                  }
+                                  val argumentsWithoutModule = parameters.joinToString { it.name }
+
+                                  addStatement("return %T($argumentsWithoutModule)", classType)
+                                }
+                                .returns(classType)
+                                .build()
+                        )
+                        .build()
+                )
+                .build()
+        )
+        .build()
+        .writeToString()
+        .replaceImports(clazz)
+
+    val directory = File(codeGenDir, packageName.replace('.', File.separatorChar))
+    val file = File(directory, "$className.kt")
+    check(file.parentFile.exists() || file.parentFile.mkdirs()) {
+      "Could not generate package directory: ${file.parentFile}"
+    }
+    file.writeText(content)
+
+    return GeneratedFile(file, content)
+  }
+}
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/KotlinPoet.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/KotlinPoet.kt
new file mode 100644
index 00000000..3359f3ea
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/KotlinPoet.kt
@@ -0,0 +1,230 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.AnvilCompilationException
+import com.squareup.anvil.compiler.AnvilComponentRegistrar
+import com.squareup.anvil.compiler.codegen.fqNameOrNull
+import com.squareup.anvil.compiler.codegen.hasAnnotation
+import com.squareup.anvil.compiler.codegen.isGenericType
+import com.squareup.anvil.compiler.codegen.isNullable
+import com.squareup.anvil.compiler.codegen.requireFqName
+import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
+import com.squareup.anvil.compiler.daggerLazyFqName
+import com.squareup.anvil.compiler.jvmSuppressWildcardsFqName
+import com.squareup.anvil.compiler.providerFqName
+import com.squareup.kotlinpoet.AnnotationSpec
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.ParameterizedTypeName
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+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.jvmSuppressWildcards
+import dagger.Lazy
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtCallableDeclaration
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtTypeArgumentList
+import org.jetbrains.kotlin.psi.KtTypeProjection
+import org.jetbrains.kotlin.psi.KtTypeReference
+import java.io.ByteArrayOutputStream
+import javax.annotation.Generated
+import javax.inject.Provider
+
+internal fun KtClassOrObject.asClassName(): ClassName =
+  ClassName.bestGuess(requireFqName().asString())
+
+internal fun KtCallableDeclaration.requireTypeName(module: ModuleDescriptor): TypeName {
+  fun fail(): Nothing = throw AnvilCompilationException(
+      message = "Couldn't resolve type of function: ${requireFqName()}",
+      element = this
+  )
+
+  val typeReference = typeReference ?: fail()
+
+  fun typeVariableName(): TypeVariableName {
+    return TypeVariableName(name = typeReference.typeElement?.text ?: fail())
+  }
+
+  if (typeReference.isGenericType()) return typeVariableName()
+
+  val fqName = typeReference.fqNameOrNull(module) ?: return typeVariableName()
+  return ClassName.bestGuess(fqName.asString())
+}
+
+internal data class Parameter(
+  val name: String,
+  val typeName: TypeName,
+  val providerTypeName: ParameterizedTypeName,
+  val lazyTypeName: ParameterizedTypeName,
+  val isWrappedInProvider: Boolean,
+  val isWrappedInLazy: Boolean
+) {
+  val originalTypeName: TypeName = when {
+    isWrappedInProvider -> providerTypeName
+    isWrappedInLazy -> lazyTypeName
+    else -> typeName
+  }
+}
+
+internal fun List<KtCallableDeclaration>.mapToParameter(module: ModuleDescriptor): List<Parameter> =
+  mapIndexed { index, parameter ->
+    val typeElement = parameter.typeReference?.typeElement
+    val typeFqName = typeElement?.fqNameOrNull(module)
+
+    val isWrappedInProvider = typeFqName == providerFqName
+    val isWrappedInLazy = typeFqName == daggerLazyFqName
+
+    val typeName = when {
+      parameter.typeReference?.isNullable() ?: false -> parameter.requireTypeName(module)
+      isWrappedInProvider || isWrappedInLazy -> {
+        TypeVariableName(
+            name = typeElement!!.children
+                .filterIsInstance<KtTypeArgumentList>()
+                .single()
+                .children
+                .filterIsInstance<KtTypeProjection>()
+                .single()
+                .children
+                .filterIsInstance<KtTypeReference>()
+                .single()
+                .text
+        )
+      }
+      else -> parameter.requireTypeName(module)
+    }.withJvmSuppressWildcardsIfNeeded(parameter)
+
+    Parameter(
+        name = "param$index",
+        typeName = typeName,
+        providerTypeName = typeName.wrapInProvider(),
+        lazyTypeName = typeName.wrapInLazy(),
+        isWrappedInProvider = isWrappedInProvider,
+        isWrappedInLazy = isWrappedInLazy
+    )
+  }
+
+internal fun <T : KtCallableDeclaration> TypeName.withJvmSuppressWildcardsIfNeeded(
+  callableDeclaration: T
+): TypeName {
+  // If the parameter is annotated with @JvmSuppressWildcards, then add the annotation
+  // to our type so that this information is forwarded when our Factory is compiled.
+  val hasJvmSuppressWildcards =
+    callableDeclaration.typeReference?.hasAnnotation(jvmSuppressWildcardsFqName) ?: false
+
+  // Add the @JvmSuppressWildcards annotation even for simple generic return types like
+  // Set<String>. This avoids some edge cases where Dagger chokes.
+  val isGenericType = callableDeclaration.typeReference?.isGenericType() ?: false
+
+  return if (hasJvmSuppressWildcards || isGenericType) this.jvmSuppressWildcards() else this
+}
+
+internal fun List<Parameter>.asArgumentList(
+  asProvider: Boolean,
+  includeModule: Boolean
+): String {
+  return this
+      .let { list ->
+        if (asProvider) {
+          list.map { parameter ->
+            when {
+              parameter.isWrappedInProvider -> parameter.name
+              parameter.isWrappedInLazy ->
+                "$daggerDoubleCheckFqNameString.lazy(${parameter.name})"
+              else -> "${parameter.name}.get()"
+            }
+          }
+        } else list.map { it.name }
+      }
+      .let {
+        if (includeModule) {
+          val result = it.toMutableList()
+          result.add(0, "module")
+          result.toList()
+        } else {
+          it
+        }
+      }
+      .joinToString()
+}
+
+private fun TypeName.wrapInProvider(): ParameterizedTypeName {
+  return Provider::class.asClassName().parameterizedBy(this)
+}
+
+private fun TypeName.wrapInLazy(): ParameterizedTypeName {
+  return Lazy::class.asClassName().parameterizedBy(this)
+}
+
+internal fun TypeSpec.Builder.addAnvilAnnotation(): TypeSpec.Builder {
+  return addAnnotation(
+      AnnotationSpec.builder(Generated::class)
+          .addMember("value = [%S]", AnvilComponentRegistrar::class.java.canonicalName)
+          .addMember("comments = %S", "https://github.com/square/anvil")
+          .build()
+  )
+}
+
+internal fun FileSpec.writeToString(): String {
+  val stream = ByteArrayOutputStream()
+  stream.writer().use {
+    writeTo(it)
+  }
+  return stream.toString()
+}
+
+/**
+ * Removes invalid imports that can be caused by [requireTypeName] and copies imports from the
+ * source file. Copying imports is necessary for star imports.
+ */
+internal fun String.replaceImports(clazz: KtClassOrObject): String {
+  val originalLines = lines()
+  val linesWithoutImports = originalLines
+      .filterNot { it.startsWith("import ") }
+      .toMutableList()
+
+  val copiedImports = clazz.containingKtFile
+      .importDirectives
+      .map { it.text.trim() }
+
+  val importDirectives = copiedImports
+      .plus(
+          originalLines
+              .filter {
+                it.startsWith("import ") && it.contains(".")
+              }
+              .map { it.trim() }
+      )
+      .distinct()
+      .toMutableList()
+
+  // Now that we copied the imports from the original file it could happen that we have ambiguous
+  // imports. Remove the import that is used with a fully qualified name elsewhere in the code or
+  // remove the copied import again if it's unused.
+  importDirectives
+      .filterNot { it.endsWith(".*") }
+      .groupBy { it.substringAfterLast(".") }
+      .filterValues { it.size > 1 }
+      .values
+      .forEach { ambiguousImports ->
+        val usedImport = ambiguousImports.firstOrNull { ambiguousImport ->
+          val fqName = ambiguousImport.substringAfter("import ")
+          linesWithoutImports.any { it.contains(fqName) }
+        }
+
+        if (usedImport != null) {
+          importDirectives.remove(usedImport)
+        } else {
+          // It's possible that the copied import was unused.
+          ambiguousImports.forEach {
+            if (it in copiedImports) {
+              importDirectives.remove(it)
+            }
+          }
+        }
+      }
+
+  linesWithoutImports.addAll(2, importDirectives.sorted())
+  return linesWithoutImports.joinToString(separator = "\n")
+}
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectorGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectorGenerator.kt
new file mode 100644
index 00000000..c3170f3f
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectorGenerator.kt
@@ -0,0 +1,187 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.codegen.CodeGenerator
+import com.squareup.anvil.compiler.codegen.CodeGenerator.GeneratedFile
+import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
+import com.squareup.anvil.compiler.codegen.hasAnnotation
+import com.squareup.anvil.compiler.codegen.requireFqName
+import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
+import com.squareup.anvil.compiler.generateClassName
+import com.squareup.anvil.compiler.injectFqName
+import com.squareup.kotlinpoet.AnnotationSpec
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier.OVERRIDE
+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.asClassName
+import com.squareup.kotlinpoet.jvm.jvmStatic
+import dagger.MembersInjector
+import dagger.internal.InjectedFieldSignature
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtClassBody
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
+import java.io.File
+import java.util.Locale.US
+
+internal class MembersInjectorGenerator : CodeGenerator {
+  override fun generateCode(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    projectFiles: Collection<KtFile>
+  ): Collection<GeneratedFile> {
+    return projectFiles
+        .asSequence()
+        .flatMap { it.classesAndInnerClasses() }
+        .mapNotNull { clazz ->
+          val injectProperties = clazz.children
+              .asSequence()
+              .filterIsInstance<KtClassBody>()
+              .flatMap { it.properties.asSequence() }
+              .filterNot { it.visibilityModifierTypeOrDefault() == KtTokens.PRIVATE_KEYWORD }
+              .filter { it.hasAnnotation(injectFqName) }
+              .toList()
+              .ifEmpty { return@mapNotNull null }
+
+          generateMembersInjectorClass(codeGenDir, module, clazz, injectProperties)
+        }
+        .toList()
+  }
+
+  @OptIn(ExperimentalStdlibApi::class)
+  private fun generateMembersInjectorClass(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    clazz: KtClassOrObject,
+    injectProperties: List<KtProperty>
+  ): GeneratedFile {
+    val packageName = clazz.containingKtFile.packageFqName.asString()
+    val className = "${clazz.generateClassName()}_MembersInjector"
+    val classType = clazz.asClassName()
+
+    val parameters = injectProperties.mapToParameter(module)
+
+    fun createArgumentList(asProvider: Boolean): String {
+      return parameters
+          .map { it.name }
+          .let { list ->
+            if (asProvider) list.map { "$it.get()" } else list
+          }
+          .joinToString()
+    }
+
+    val memberInjectorClass = ClassName(packageName, className)
+
+    val content = FileSpec.builder(packageName, className)
+        .addType(
+            TypeSpec
+                .classBuilder(memberInjectorClass)
+                .addAnvilAnnotation()
+                .addSuperinterface(MembersInjector::class.asClassName().parameterizedBy(classType))
+                .apply {
+                  primaryConstructor(
+                      FunSpec.constructorBuilder()
+                          .apply {
+                            parameters.forEach { parameter ->
+                              addParameter(parameter.name, parameter.providerTypeName)
+                            }
+                          }
+                          .build()
+                  )
+
+                  parameters.forEach { parameter ->
+                    addProperty(
+                        PropertySpec.builder(parameter.name, parameter.providerTypeName)
+                            .initializer(parameter.name)
+                            .addModifiers(PRIVATE)
+                            .build()
+                    )
+                  }
+                }
+                .addFunction(
+                    FunSpec.builder("injectMembers")
+                        .addModifiers(OVERRIDE)
+                        .addParameter("instance", classType)
+                        .apply {
+                          parameters.forEachIndexed { index, parameter ->
+                            val property = injectProperties[index]
+                            val propertyName = property.nameAsSafeName.asString()
+
+                            addStatement(
+                                "inject${propertyName.capitalize(US)}(instance, ${
+                                  when {
+                                    parameter.isWrappedInProvider -> parameter.name
+                                    parameter.isWrappedInLazy ->
+                                      "$daggerDoubleCheckFqNameString.lazy(${parameter.name})"
+                                    else -> parameter.name + ".get()"
+                                  }
+                                })"
+                            )
+                          }
+                        }
+                        .build()
+                )
+                .addType(
+                    TypeSpec
+                        .companionObjectBuilder()
+                        .addFunction(
+                            FunSpec.builder("create")
+                                .jvmStatic()
+                                .apply {
+                                  parameters.forEach { parameter ->
+                                    addParameter(parameter.name, parameter.providerTypeName)
+                                  }
+
+                                  addStatement(
+                                      "return %T(${createArgumentList(false)})",
+                                      memberInjectorClass
+                                  )
+                                }
+                                .returns(memberInjectorClass)
+                                .build()
+                        )
+                        .apply {
+                          parameters.forEachIndexed { index, parameter ->
+                            val property = injectProperties[index]
+                            val propertyName = property.nameAsSafeName.asString()
+
+                            addFunction(
+                                FunSpec.builder("inject${propertyName.capitalize(US)}")
+                                    .jvmStatic()
+                                    .addAnnotation(
+                                        AnnotationSpec.builder(InjectedFieldSignature::class)
+                                            .addMember("%S", property.requireFqName())
+                                            .build()
+                                    )
+                                    .addParameter("instance", classType)
+                                    .addParameter(propertyName, parameter.originalTypeName)
+                                    .addStatement("instance.$propertyName = $propertyName")
+                                    .build()
+                            )
+                          }
+                        }
+                        .build()
+                )
+                .build()
+        )
+        .build()
+        .writeToString()
+        .replaceImports(clazz)
+
+    val directory = File(codeGenDir, packageName.replace('.', File.separatorChar))
+    val file = File(directory, "$className.kt")
+    check(file.parentFile.exists() || file.parentFile.mkdirs()) {
+      "Could not generate package directory: ${file.parentFile}"
+    }
+    file.writeText(content)
+
+    return GeneratedFile(file, content)
+  }
+}
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ProvidesMethodFactoryGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ProvidesMethodFactoryGenerator.kt
new file mode 100644
index 00000000..c1affc59
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/ProvidesMethodFactoryGenerator.kt
@@ -0,0 +1,245 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.codegen.CodeGenerator
+import com.squareup.anvil.compiler.codegen.CodeGenerator.GeneratedFile
+import com.squareup.anvil.compiler.codegen.classesAndInnerClasses
+import com.squareup.anvil.compiler.codegen.functions
+import com.squareup.anvil.compiler.codegen.hasAnnotation
+import com.squareup.anvil.compiler.codegen.isNullable
+import com.squareup.anvil.compiler.codegen.requireFqName
+import com.squareup.anvil.compiler.daggerModuleFqName
+import com.squareup.anvil.compiler.daggerProvidesFqName
+import com.squareup.anvil.compiler.generateClassName
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier.OVERRIDE
+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.asClassName
+import com.squareup.kotlinpoet.jvm.jvmStatic
+import dagger.internal.Factory
+import dagger.internal.Preconditions
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.parents
+import java.io.File
+import java.util.Locale.US
+
+internal class ProvidesMethodFactoryGenerator : CodeGenerator {
+  override fun generateCode(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    projectFiles: Collection<KtFile>
+  ): Collection<GeneratedFile> {
+    return projectFiles
+        .asSequence()
+        .flatMap { it.classesAndInnerClasses() }
+        .filter { it.hasAnnotation(daggerModuleFqName) }
+        .flatMap { clazz ->
+          clazz
+              .functions(includeCompanionObjects = true)
+              .asSequence()
+              .filter { it.hasAnnotation(daggerProvidesFqName) }
+              .map { function ->
+                generateFactoryClass(codeGenDir, module, clazz, function)
+              }
+        }
+        .toList()
+  }
+
+  @OptIn(ExperimentalStdlibApi::class)
+  private fun generateFactoryClass(
+    codeGenDir: File,
+    module: ModuleDescriptor,
+    clazz: KtClassOrObject,
+    function: KtNamedFunction
+  ): GeneratedFile {
+    val isCompanionObject = function.parents
+        .filterIsInstance<KtObjectDeclaration>()
+        .firstOrNull()
+        ?.isCompanion()
+        ?: false
+    val isObject = isCompanionObject || clazz is KtObjectDeclaration
+
+    val packageName = clazz.containingKtFile.packageFqName.asString()
+    val className = "${clazz.generateClassName()}_" +
+        (if (isCompanionObject) "Companion_" else "") +
+        "${function.requireFqName().shortName().asString().capitalize(US)}Factory"
+    val functionName = function.nameAsSafeName.asString()
+
+    val parameters = function.valueParameters.mapToParameter(module)
+
+    // This is a little hacky. The typeElement could be "String", "Abc", etc., but also a generic
+    // type like "List<String>". We simply copy this literal as return type into our generated
+    // code. Kotlinpoet will add an import for this literal like "import String", which we later
+    // will remove again.
+    //
+    // This solution is a lot easier than trying to resolve all FqNames for each type.
+    val returnType = function.requireTypeName(module)
+        .withJvmSuppressWildcardsIfNeeded(function)
+    val returnTypeIsNullable = function.typeReference?.isNullable() ?: false
+
+    val factoryClass = ClassName(packageName, className)
+    val moduleClass = clazz.asClassName()
+
+    val content = FileSpec.builder(packageName, className)
+        .addType(
+            TypeSpec
+                .classBuilder(factoryClass)
+                .addAnvilAnnotation()
+                .addSuperinterface(Factory::class.asClassName().parameterizedBy(returnType))
+                .apply {
+                  if (!isObject || parameters.isNotEmpty()) {
+                    primaryConstructor(
+                        FunSpec.constructorBuilder()
+                            .apply {
+                              if (!isObject) {
+                                addParameter("module", moduleClass)
+                              }
+                              parameters.forEach { parameter ->
+                                addParameter(parameter.name, parameter.providerTypeName)
+                              }
+                            }
+                            .build()
+                    )
+
+                    if (!isObject) {
+                      addProperty(
+                          PropertySpec.builder("module", moduleClass)
+                              .initializer("module")
+                              .addModifiers(PRIVATE)
+                              .build()
+                      )
+                    }
+
+                    parameters.forEach { parameter ->
+                      addProperty(
+                          PropertySpec.builder(parameter.name, parameter.providerTypeName)
+                              .initializer(parameter.name)
+                              .addModifiers(PRIVATE)
+                              .build()
+                      )
+                    }
+                  }
+                }
+                .addFunction(
+                    FunSpec.builder("get")
+                        .addModifiers(OVERRIDE)
+                        .returns(returnType)
+                        .apply {
+                          val argumentList = parameters.asArgumentList(
+                              asProvider = true,
+                              includeModule = !isObject
+                          )
+                          addStatement("return $functionName($argumentList)")
+                        }
+                        .build()
+                )
+                .addType(
+                    TypeSpec
+                        .companionObjectBuilder()
+                        .apply {
+                          if (isObject && parameters.isEmpty()) {
+                            addProperty(
+                                PropertySpec.builder("instance", factoryClass)
+                                    .addModifiers(PRIVATE)
+                                    .initializer("%T()", factoryClass)
+                                    .build()
+                            )
+                          }
+                        }
+                        .addFunction(
+                            FunSpec.builder("create")
+                                .jvmStatic()
+                                .apply {
+                                  if (isObject && parameters.isEmpty()) {
+                                    addStatement("return instance")
+                                  } else {
+                                    if (!isObject) {
+                                      addParameter("module", moduleClass)
+                                    }
+                                    parameters.forEach { parameter ->
+                                      addParameter(parameter.name, parameter.providerTypeName)
+                                    }
+
+                                    val argumentList = parameters.asArgumentList(
+                                        asProvider = false,
+                                        includeModule = !isObject
+                                    )
+
+                                    addStatement("return %T($argumentList)", factoryClass)
+                                  }
+                                }
+                                .returns(factoryClass)
+                                .build()
+                        )
+                        .addFunction(
+                            FunSpec.builder(functionName)
+                                .jvmStatic()
+                                .apply {
+                                  if (!isObject) {
+                                    addParameter("module", moduleClass)
+                                  }
+
+                                  parameters.forEach { parameter ->
+                                    addParameter(
+                                        name = parameter.name,
+                                        type = parameter.originalTypeName
+                                    )
+                                  }
+                                  val argumentsWithoutModule = parameters.joinToString { it.name }
+
+                                  when {
+                                    isObject && returnTypeIsNullable ->
+                                      addStatement(
+                                          "return %T.$functionName($argumentsWithoutModule)",
+                                          moduleClass
+                                      )
+                                    isObject && !returnTypeIsNullable ->
+                                      addStatement(
+                                          "return %T.checkNotNull(%T.$functionName" +
+                                              "($argumentsWithoutModule), %S)",
+                                          Preconditions::class,
+                                          moduleClass,
+                                          "Cannot return null from a non-@Nullable @Provides method"
+                                      )
+                                    !isObject && returnTypeIsNullable ->
+                                      addStatement(
+                                          "return module.$functionName($argumentsWithoutModule)"
+                                      )
+                                    !isObject && !returnTypeIsNullable ->
+                                      addStatement(
+                                          "return %T.checkNotNull(module.$functionName" +
+                                              "($argumentsWithoutModule), %S)",
+                                          Preconditions::class,
+                                          "Cannot return null from a non-@Nullable @Provides method"
+                                      )
+                                  }
+                                }
+                                .returns(returnType)
+                                .build()
+                        )
+                        .build()
+                )
+                .build()
+        )
+        .build()
+        .writeToString()
+        .replaceImports(clazz)
+
+    val directory = File(codeGenDir, packageName.replace('.', File.separatorChar))
+    val file = File(directory, "$className.kt")
+    check(file.parentFile.exists() || file.parentFile.mkdirs()) {
+      "Could not generate package directory: ${file.parentFile}"
+    }
+    file.writeText(content)
+
+    return GeneratedFile(file, content)
+  }
+}
diff --git a/dependencies.gradle b/dependencies.gradle
index 8395fa81..c1b42675 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -47,6 +47,8 @@ ext {
       junit: "junit:junit:4.13",
 
       kotlin: [
+          // TODO: when upgrading run tests in CI with with Kotlin 1.4 again. The compile testing
+          //  library doesn't support Kotlin 1.4, yet.
           compile_testing: "com.github.tschuchortdev:kotlin-compile-testing:1.2.9",
           compiler: "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion",
           gradle_plugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion",
@@ -54,6 +56,8 @@ ext {
           stdlib: "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion",
       ],
 
+      kotlinpoet: "com.squareup:kotlinpoet:1.6.0",
+
       truth: "com.google.truth:truth:1.0.1",
   ]
 }
diff --git a/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilExtension.kt b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilExtension.kt
new file mode 100644
index 00000000..98938df9
--- /dev/null
+++ b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilExtension.kt
@@ -0,0 +1,17 @@
+package com.squareup.anvil.plugin
+
+open class AnvilExtension {
+  /**
+   * Highly experimental feature that allows you to use Anvil to generate Factory classes that
+   * usually Dagger would generate for @Provides methods and @Inject constructors.
+   *
+   * The benefit of this feature is that you don't need to enable the Dagger compiler in this
+   * module. That often means you can skip KAPT and the stub generating task. On top of it Anvil
+   * generates Kotlin instead of Java code, which also allows Gradle to skip the Java compilation
+   * task. The result are faster builds.
+   *
+   * This feature can only be enabled in Gradle modules that don't compile any Dagger component.
+   * By default this feature is disabled.
+   */
+  var generateDaggerFactories = false
+}
diff --git a/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilPlugin.kt b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilPlugin.kt
index 660829ba..ae5fca42 100644
--- a/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilPlugin.kt
+++ b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilPlugin.kt
@@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicBoolean
 
 open class AnvilPlugin : Plugin<Project> {
   override fun apply(project: Project) {
+    project.extensions.create("anvil", AnvilExtension::class.java)
+
     val once = AtomicBoolean()
 
     fun PluginManager.withPluginOnce(
diff --git a/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilSubplugin.kt b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilSubplugin.kt
index 4202cfc7..6af2bd1f 100644
--- a/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilSubplugin.kt
+++ b/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilSubplugin.kt
@@ -56,6 +56,14 @@ class AnvilSubplugin : KotlinGradleSubplugin<AbstractCompile> {
       )
     }
 
-    return listOf(srcGenDirOption)
+    val extension = project.extensions.findByType(AnvilExtension::class.java) ?: AnvilExtension()
+
+    return listOf(
+        srcGenDirOption,
+        SubpluginOption(
+            key = "generate-dagger-factories",
+            value = extension.generateDaggerFactories.toString()
+        )
+    )
   }
 }
diff --git a/sample/library/build.gradle b/sample/library/build.gradle
index 22d077d8..de834723 100644
--- a/sample/library/build.gradle
+++ b/sample/library/build.gradle
@@ -1,13 +1,14 @@
 apply plugin: 'org.jetbrains.kotlin.jvm'
-apply plugin: 'org.jetbrains.kotlin.kapt'
 apply plugin: 'com.squareup.anvil'
 
+anvil {
+  generateDaggerFactories = true
+}
+
 dependencies {
   api project(':sample:scopes')
   api deps.dagger2.dagger
   api deps.kotlin.stdlib
-
-  kapt deps.dagger2.compiler
 }
 
 //noinspection UnnecessaryQualifiedReference


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/ReflectionUtils.kt b/compiler/src/test/java/com/squareup/anvil/compiler/ReflectionUtils.kt
new file mode 100644
index 00000000..d082c0ff
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/ReflectionUtils.kt
@@ -0,0 +1,6 @@
+package com.squareup.anvil.compiler
+
+import java.lang.reflect.Method
+import java.lang.reflect.Modifier
+
+val Method.isStatic: Boolean get() = Modifier.isStatic(modifiers)
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
index 7cb6f528..204d5868 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
@@ -9,6 +9,7 @@ import com.tschuchort.compiletesting.SourceFile
 import dagger.Component
 import dagger.Module
 import dagger.Subcomponent
+import dagger.internal.codegen.ComponentProcessor
 import org.jetbrains.kotlin.name.FqName
 import org.junit.Assume.assumeTrue
 import java.io.File
@@ -17,6 +18,8 @@ import kotlin.reflect.KClass
 
 internal fun compile(
   source: String,
+  enableDaggerAnnotationProcessor: Boolean = false,
+  generateDaggerFactories: Boolean = false,
   block: Result.() -> Unit = { }
 ): Result {
   return KotlinCompilation()
@@ -25,6 +28,10 @@ internal fun compile(
         useIR = false
         inheritClassPath = true
 
+        if (enableDaggerAnnotationProcessor) {
+          annotationProcessors = listOf(ComponentProcessor())
+        }
+
         val commandLineProcessor = AnvilCommandLineProcessor()
         commandLineProcessors = listOf(commandLineProcessor)
 
@@ -33,6 +40,11 @@ internal fun compile(
                 pluginId = commandLineProcessor.pluginId,
                 optionName = srcGenDirName,
                 optionValue = File(workingDir, "build/anvil").absolutePath
+            ),
+            PluginOption(
+                pluginId = commandLineProcessor.pluginId,
+                optionName = generateDaggerFactoriesName,
+                optionValue = generateDaggerFactories.toString()
             )
         )
 
@@ -90,6 +102,9 @@ internal val Result.daggerModule4: Class<*>
 internal val Result.innerModule: Class<*>
   get() = classLoader.loadClass("com.squareup.test.ComponentInterface\$InnerModule")
 
+internal val Result.injectClass: Class<*>
+  get() = classLoader.loadClass("com.squareup.test.InjectClass")
+
 @OptIn(ExperimentalStdlibApi::class)
 internal val Class<*>.hintContributes: KClass<*>?
   get() = contributedProperties(HINT_CONTRIBUTES_PACKAGE_PREFIX)
@@ -120,6 +135,35 @@ internal val Class<*>.hintBindingScope: KClass<*>?
       ?.also { assertThat(it.size).isEqualTo(1) }
       ?.first()
 
+@OptIn(ExperimentalStdlibApi::class)
+internal fun Class<*>.moduleFactoryClass(
+  providerMethodName: String,
+  companion: Boolean = false
+): Class<*> {
+  val companionString = if (companion) "_Companion" else ""
+  val enclosingClassString = enclosingClass?.let { "${it.simpleName}_" } ?: ""
+
+  return classLoader.loadClass(
+      "${`package`.name}.$enclosingClassString$simpleName$companionString" +
+          "_${providerMethodName.capitalize(US)}Factory"
+  )
+}
+
+@OptIn(ExperimentalStdlibApi::class)
+internal fun Class<*>.factoryClass(): Class<*> {
+  val enclosingClassString = enclosingClass?.let { "${it.simpleName}_" } ?: ""
+
+  return classLoader.loadClass("${`package`.name}.$enclosingClassString${simpleName}_Factory")
+}
+
+@OptIn(ExperimentalStdlibApi::class)
+internal fun Class<*>.membersInjector(): Class<*> {
+  val enclosingClassString = enclosingClass?.let { "${it.simpleName}_" } ?: ""
+
+  return classLoader.loadClass("${`package`.name}." +
+      "$enclosingClassString${simpleName}_MembersInjector")
+}
+
 @OptIn(ExperimentalStdlibApi::class)
 private fun Class<*>.contributedProperties(packagePrefix: String): List<KClass<*>>? {
   // The capitalize() doesn't make sense, I don't know where this is coming from. Maybe it's a
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt
new file mode 100644
index 00000000..2ce944e6
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt
@@ -0,0 +1,114 @@
+package com.squareup.anvil.compiler.dagger
+
+import com.google.common.truth.Truth.assertThat
+import com.tschuchort.compiletesting.KotlinCompilation.ExitCode.COMPILATION_ERROR
+import com.tschuchort.compiletesting.KotlinCompilation.Result
+import org.junit.Test
+
+class ComponentDetectorCheckTest {
+
+  @Test fun `a Dagger component causes an error`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Component
+        
+        @Component
+        interface ComponentInterface
+    """
+    ) {
+      assertThat(exitCode).isEqualTo(COMPILATION_ERROR)
+      // Position to the class.
+      assertThat(messages).contains("Source.kt: (5, 1")
+      assertThat(messages).contains(
+          "Anvil cannot generate the code for Dagger components or subcomponents. In these " +
+              "cases the Dagger annotation processor is required. Enabling the Dagger " +
+              "annotation processor and turning on Anvil to generate Dagger factories is " +
+              "redundant. Set 'generateDaggerFactories' to false."
+      )
+    }
+  }
+
+  @Test fun `a Dagger subcomponent causes an error`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Subcomponent
+        
+        @Subcomponent
+        interface ComponentInterface
+    """
+    ) {
+      assertThat(exitCode).isEqualTo(COMPILATION_ERROR)
+      // Position to the class.
+      assertThat(messages).contains("Source.kt: (5, 1")
+      assertThat(messages).contains(
+          "Anvil cannot generate the code for Dagger components or subcomponents. In these " +
+              "cases the Dagger annotation processor is required. Enabling the Dagger " +
+              "annotation processor and turning on Anvil to generate Dagger factories is " +
+              "redundant. Set 'generateDaggerFactories' to false."
+      )
+    }
+  }
+
+  @Test fun `a Dagger component causes an error inner class`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Component
+        
+        class OuterClass {
+          @Component
+          interface ComponentInterface
+        }
+    """
+    ) {
+      assertThat(exitCode).isEqualTo(COMPILATION_ERROR)
+      // Position to the class.
+      assertThat(messages).contains("Source.kt: (6, 3")
+      assertThat(messages).contains(
+          "Anvil cannot generate the code for Dagger components or subcomponents. In these " +
+              "cases the Dagger annotation processor is required. Enabling the Dagger " +
+              "annotation processor and turning on Anvil to generate Dagger factories is " +
+              "redundant. Set 'generateDaggerFactories' to false."
+      )
+    }
+  }
+
+  @Test fun `a Dagger subcomponent causes an error inner class`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Subcomponent
+        
+        class OuterClass {
+          @Subcomponent
+          interface ComponentInterface
+        }
+    """
+    ) {
+      assertThat(exitCode).isEqualTo(COMPILATION_ERROR)
+      // Position to the class.
+      assertThat(messages).contains("Source.kt: (6, 3")
+      assertThat(messages).contains(
+          "Anvil cannot generate the code for Dagger components or subcomponents. In these " +
+              "cases the Dagger annotation processor is required. Enabling the Dagger " +
+              "annotation processor and turning on Anvil to generate Dagger factories is " +
+              "redundant. Set 'generateDaggerFactories' to false."
+      )
+    }
+  }
+
+  private fun compile(
+    source: String,
+    block: Result.() -> Unit = { }
+  ): Result = com.squareup.anvil.compiler.compile(
+      source = source,
+      generateDaggerFactories = true,
+      block = block
+  )
+}
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/Factory.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/Factory.kt
new file mode 100644
index 00000000..571321bf
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/Factory.kt
@@ -0,0 +1,6 @@
+@file:Suppress("unused")
+
+package com.squareup.anvil.compiler.dagger
+
+// This Factory is used in some of the unit tests. Don't touch it.
+object Factory
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
new file mode 100644
index 00000000..59d2e348
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
@@ -0,0 +1,608 @@
+package com.squareup.anvil.compiler.dagger
+
+import com.google.common.truth.Truth.assertThat
+import com.squareup.anvil.compiler.factoryClass
+import com.squareup.anvil.compiler.injectClass
+import com.squareup.anvil.compiler.isStatic
+import com.tschuchort.compiletesting.KotlinCompilation.Result
+import dagger.Lazy
+import dagger.internal.Factory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+import java.io.File
+import javax.inject.Provider
+
+@Suppress("UNCHECKED_CAST")
+@RunWith(Parameterized::class)
+class InjectConstructorFactoryGeneratorTest(
+  private val useDagger: Boolean
+) {
+
+  companion object {
+    @Parameters(name = "Use Dagger: {0}")
+    @JvmStatic fun useDagger(): Collection<Any> {
+      return listOf(true, false)
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor without arguments`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  @Override
+  public InjectClass get() {
+    return newInstance();
+  }
+
+  public static InjectClass_Factory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static InjectClass newInstance() {
+    return new InjectClass();
+  }
+
+  private static final class InstanceHolder {
+    private static final InjectClass_Factory INSTANCE = new InjectClass_Factory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        class InjectClass @Inject constructor()
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val instance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null)
+
+      assertThat(instance).isNotNull()
+      assertThat((factoryInstance as Factory<*>).get()).isNotNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor with arguments`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<Integer> p1_52215Provider;
+
+  public InjectClass_Factory(Provider<String> stringProvider, Provider<Integer> p1_52215Provider) {
+    this.stringProvider = stringProvider;
+    this.p1_52215Provider = p1_52215Provider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(stringProvider.get(), p1_52215Provider.get());
+  }
+
+  public static InjectClass_Factory create(Provider<String> stringProvider,
+      Provider<Integer> p1_52215Provider) {
+    return new InjectClass_Factory(stringProvider, p1_52215Provider);
+  }
+
+  public static InjectClass newInstance(String string, int p1_52215) {
+    return new InjectClass(string, p1_52215);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        data class InjectClass @Inject constructor(
+          val string: String, 
+          val int: Int
+        )
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { "abc" }, Provider { 1 })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null, "abc", 1)
+      val getInstance = (factoryInstance as Factory<*>).get()
+
+      assertThat(newInstance).isNotNull()
+      assertThat(getInstance).isNotNull()
+
+      assertThat(newInstance).isEqualTo(getInstance)
+      assertThat(newInstance).isNotSameInstanceAs(getInstance)
+    }
+  }
+
+  @Test
+  fun `a factory class is generated for an inject constructor with Provider and Lazy arguments`() {
+    /*
+package com.squareup.test;
+
+import dagger.Lazy;
+import dagger.internal.DoubleCheck;
+import dagger.internal.Factory;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<String> stringProvider2;
+
+  private final Provider<List<String>> stringListProvider;
+
+  private final Provider<String> stringProvider3;
+
+  public InjectClass_Factory(Provider<String> stringProvider, Provider<String> stringProvider2,
+      Provider<List<String>> stringListProvider, Provider<String> stringProvider3) {
+    this.stringProvider = stringProvider;
+    this.stringProvider2 = stringProvider2;
+    this.stringListProvider = stringListProvider;
+    this.stringProvider3 = stringProvider3;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(stringProvider.get(), stringProvider2, stringListProvider, DoubleCheck.lazy(stringProvider3));
+  }
+
+  public static InjectClass_Factory create(Provider<String> stringProvider,
+      Provider<String> stringProvider2, Provider<List<String>> stringListProvider,
+      Provider<String> stringProvider3) {
+    return new InjectClass_Factory(stringProvider, stringProvider2, stringListProvider, stringProvider3);
+  }
+
+  public static InjectClass newInstance(String string, Provider<String> stringProvider,
+      Provider<List<String>> stringListProvider, Lazy<String> lazyString) {
+    return new InjectClass(string, stringProvider, stringListProvider, lazyString);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Lazy
+        import javax.inject.Inject
+        import javax.inject.Provider
+        
+        class InjectClass @Inject constructor(
+          val string: String, 
+          val stringProvider: Provider<String>,
+          val stringListProvider: Provider<List<String>>,
+          val lazyString: Lazy<String>
+        ) {
+          override fun equals(other: Any?): Boolean {
+            return toString() == other.toString()
+          }
+          override fun toString(): String {
+           return string + stringProvider.get() + 
+               stringListProvider.get()[0] + lazyString.get()
+          }
+        }
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(
+              Provider::class.java, Provider::class.java, Provider::class.java, Provider::class.java
+          )
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { "a" }, Provider { "b" }, Provider { listOf("c") },
+              Provider { "d" })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null, "a", Provider { "b" }, Provider { listOf("c") }, Lazy { "d" })
+      val getInstance = (factoryInstance as Factory<*>).get()
+
+      assertThat(newInstance).isNotNull()
+      assertThat(getInstance).isNotNull()
+
+      assertThat(newInstance).isEqualTo(getInstance)
+      assertThat(newInstance).isNotSameInstanceAs(getInstance)
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor with star imports`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import error.NonExistentClass;
+import java.io.File;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<File> fileProvider;
+
+  private final Provider<NonExistentClass> pathProvider;
+
+  public InjectClass_Factory(Provider<File> fileProvider, Provider<NonExistentClass> pathProvider) {
+    this.fileProvider = fileProvider;
+    this.pathProvider = pathProvider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(fileProvider.get(), pathProvider.get());
+  }
+
+  public static InjectClass_Factory create(Provider<File> fileProvider,
+      Provider<NonExistentClass> pathProvider) {
+    return new InjectClass_Factory(fileProvider, pathProvider);
+  }
+
+  public static InjectClass newInstance(File file, NonExistentClass path) {
+    return new InjectClass(file, path);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import java.io.*
+        import java.nio.file.*
+        import javax.inject.*
+        
+        data class InjectClass @Inject constructor(
+          val file: File, 
+          val path: Path
+        )
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { File("") }, Provider { File("").toPath() })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null, File(""), File("").toPath())
+      val getInstance = (factoryInstance as Factory<*>).get()
+
+      assertThat(newInstance).isNotNull()
+      assertThat(getInstance).isNotNull()
+
+      assertThat(newInstance).isEqualTo(getInstance)
+      assertThat(newInstance).isNotSameInstanceAs(getInstance)
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor with generic arguments`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import kotlin.Pair;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<Pair<Pair<String, Integer>, ? extends List<String>>> pairProvider;
+
+  private final Provider<Set<String>> setProvider;
+
+  public InjectClass_Factory(
+      Provider<Pair<Pair<String, Integer>, ? extends List<String>>> pairProvider,
+      Provider<Set<String>> setProvider) {
+    this.pairProvider = pairProvider;
+    this.setProvider = setProvider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(pairProvider.get(), setProvider.get());
+  }
+
+  public static InjectClass_Factory create(
+      Provider<Pair<Pair<String, Integer>, ? extends List<String>>> pairProvider,
+      Provider<Set<String>> setProvider) {
+    return new InjectClass_Factory(pairProvider, setProvider);
+  }
+
+  public static InjectClass newInstance(Pair<Pair<String, Integer>, ? extends List<String>> pair,
+      Set<String> set) {
+    return new InjectClass(pair, set);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        data class InjectClass @Inject constructor(
+          val pair: Pair<Pair<String, Int>, List<String>>, 
+          val set: Set<String>
+        )
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { Pair(Pair("a", 1), listOf("b")) }, Provider { setOf("c") })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null, Pair(Pair("a", 1), listOf("b")), setOf("c"))
+      val getInstance = (factoryInstance as Factory<*>).get()
+
+      assertThat(newInstance).isNotNull()
+      assertThat(getInstance).isNotNull()
+
+      assertThat(newInstance).isEqualTo(getInstance)
+      assertThat(newInstance).isNotSameInstanceAs(getInstance)
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor inner class`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class OuterClass_InjectClass_Factory implements Factory<OuterClass.InjectClass> {
+  @Override
+  public OuterClass.InjectClass get() {
+    return newInstance();
+  }
+
+  public static OuterClass_InjectClass_Factory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static OuterClass.InjectClass newInstance() {
+    return new OuterClass.InjectClass();
+  }
+
+  private static final class InstanceHolder {
+    private static final OuterClass_InjectClass_Factory INSTANCE = new OuterClass_InjectClass_Factory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        class OuterClass {
+          class InjectClass @Inject constructor()
+        }
+    """
+    ) {
+      val injectClass = classLoader.loadClass("com.squareup.test.OuterClass\$InjectClass")
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val instance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null)
+
+      assertThat(instance).isNotNull()
+      assertThat((factoryInstance as Factory<*>).get()).isNotNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for an inject constructor with function arguments`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import kotlin.jvm.functions.Function1;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<Set<Function1<List<String>, List<String>>>> setProvider;
+
+  public InjectClass_Factory(Provider<String> stringProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider) {
+    this.stringProvider = stringProvider;
+    this.setProvider = setProvider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(stringProvider.get(), setProvider.get());
+  }
+
+  public static InjectClass_Factory create(Provider<String> stringProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider) {
+    return new InjectClass_Factory(stringProvider, setProvider);
+  }
+
+  public static InjectClass newInstance(String string,
+      Set<Function1<List<String>, List<String>>> set) {
+    return new InjectClass(string, set);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        typealias StringList = List<String>
+        
+        data class InjectClass @Inject constructor(
+          val string: String, 
+          val set: @JvmSuppressWildcards Set<(StringList) -> StringList>
+        )
+    """
+    ) {
+      val factoryClass = injectClass.factoryClass()
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { "a" }, Provider { setOf { _: List<String> -> listOf("b") } })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val newInstance = staticMethods.single { it.name == "newInstance" }
+          .invoke(null, "a", setOf { _: List<String> -> listOf("b") })
+      val getInstance = (factoryInstance as Factory<*>).get()
+
+      assertThat(newInstance).isNotNull()
+      assertThat(getInstance).isNotNull()
+
+      assertThat(newInstance).isNotSameInstanceAs(getInstance)
+    }
+  }
+
+  private fun compile(
+    source: String,
+    block: Result.() -> Unit = { }
+  ): Result = com.squareup.anvil.compiler.compile(
+      source = source,
+      enableDaggerAnnotationProcessor = useDagger,
+      generateDaggerFactories = !useDagger,
+      block = block
+  )
+}
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
new file mode 100644
index 00000000..d129302b
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
@@ -0,0 +1,580 @@
+package com.squareup.anvil.compiler.dagger
+
+import com.google.common.truth.Truth.assertThat
+import com.squareup.anvil.compiler.injectClass
+import com.squareup.anvil.compiler.isStatic
+import com.squareup.anvil.compiler.membersInjector
+import com.tschuchort.compiletesting.KotlinCompilation.Result
+import dagger.Lazy
+import dagger.MembersInjector
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+import java.io.File
+import java.lang.reflect.Method
+import java.util.Locale.US
+import javax.inject.Provider
+
+@Suppress("UNCHECKED_CAST")
+@RunWith(Parameterized::class)
+class MembersInjectorGeneratorTest(
+  private val useDagger: Boolean
+) {
+
+  companion object {
+    @Parameters(name = "Use Dagger: {0}")
+    @JvmStatic fun useDagger(): Collection<Any> {
+      return listOf(true, false)
+    }
+  }
+
+  @Test fun `a factory class is generated for a field injection`() {
+    /*
+package com.squareup.test;
+
+import dagger.MembersInjector;
+import dagger.internal.InjectedFieldSignature;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import kotlin.Pair;
+import kotlin.jvm.functions.Function1;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_MembersInjector implements MembersInjector<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<CharSequence> charSequenceProvider;
+
+  private final Provider<List<String>> listProvider;
+
+  private final Provider<Pair<Pair<String, Integer>, ? extends Set<String>>> pairProvider;
+
+  private final Provider<Set<Function1<List<String>, List<String>>>> setProvider;
+
+  public InjectClass_MembersInjector(Provider<String> stringProvider,
+      Provider<CharSequence> charSequenceProvider, Provider<List<String>> listProvider,
+      Provider<Pair<Pair<String, Integer>, ? extends Set<String>>> pairProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider) {
+    this.stringProvider = stringProvider;
+    this.charSequenceProvider = charSequenceProvider;
+    this.listProvider = listProvider;
+    this.pairProvider = pairProvider;
+    this.setProvider = setProvider;
+  }
+
+  public static MembersInjector<InjectClass> create(Provider<String> stringProvider,
+      Provider<CharSequence> charSequenceProvider, Provider<List<String>> listProvider,
+      Provider<Pair<Pair<String, Integer>, ? extends Set<String>>> pairProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider) {
+    return new InjectClass_MembersInjector(stringProvider, charSequenceProvider, listProvider, pairProvider, setProvider);}
+
+  @Override
+  public void injectMembers(InjectClass instance) {
+    injectString(instance, stringProvider.get());
+    injectCharSequence(instance, charSequenceProvider.get());
+    injectList(instance, listProvider.get());
+    injectPair(instance, pairProvider.get());
+    injectSet(instance, setProvider.get());
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.string")
+  public static void injectString(InjectClass instance, String string) {
+    instance.string = string;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.charSequence")
+  public static void injectCharSequence(InjectClass instance, CharSequence charSequence) {
+    instance.charSequence = charSequence;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.list")
+  public static void injectList(InjectClass instance, List<String> list) {
+    instance.list = list;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.pair")
+  public static void injectPair(InjectClass instance,
+      Pair<Pair<String, Integer>, ? extends Set<String>> pair) {
+    instance.pair = pair;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.set")
+  public static void injectSet(InjectClass instance,
+      Set<Function1<List<String>, List<String>>> set) {
+    instance.set = set;
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        typealias StringList = List<String>
+        
+        class InjectClass {
+          @Inject lateinit var string: String
+          @Inject lateinit var charSequence: CharSequence
+          @Inject lateinit var list: List<String>
+          @Inject lateinit var pair: Pair<Pair<String, Int>, Set<String>>
+          @Inject lateinit var set: @JvmSuppressWildcards Set<(StringList) -> StringList>
+          
+          override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+        
+            other as InjectClass
+        
+            if (string != other.string) return false
+            if (charSequence != other.charSequence) return false
+            if (list != other.list) return false
+            if (pair != other.pair) return false
+            if (set.single().invoke(emptyList())[0] != other.set.single().invoke(emptyList())[0]) return false
+        
+            return true
+          }
+        
+          override fun hashCode(): Int {
+            var result = string.hashCode()
+            result = 31 * result + charSequence.hashCode()
+            result = 31 * result + list.hashCode()
+            result = 31 * result + pair.hashCode()
+            result = 31 * result + set.single().invoke(emptyList())[0].hashCode()
+            return result
+          }
+        }
+    """
+    ) {
+      val membersInjector = injectClass.membersInjector()
+
+      val constructor = membersInjector.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(
+              Provider::class.java, Provider::class.java, Provider::class.java,
+              Provider::class.java, Provider::class.java
+          )
+
+      val membersInjectorInstance = constructor
+          .newInstance(
+              Provider { "a" }, Provider<CharSequence> { "b" }, Provider { listOf("c") },
+              Provider { Pair(Pair("a", 1), setOf("b")) },
+              Provider { setOf { _: List<String> -> listOf("d") } }
+          )
+          as MembersInjector<Any>
+
+      val injectInstanceConstructor = injectClass.newInstance()
+      membersInjectorInstance.injectMembers(injectInstanceConstructor)
+
+      val injectInstanceStatic = injectClass.newInstance()
+
+      membersInjector.staticInjectMethod("string")
+          .invoke(null, injectInstanceStatic, "a")
+      membersInjector.staticInjectMethod("charSequence")
+          .invoke(null, injectInstanceStatic, "b" as CharSequence)
+      membersInjector.staticInjectMethod("list")
+          .invoke(null, injectInstanceStatic, listOf("c"))
+      membersInjector.staticInjectMethod("pair")
+          .invoke(null, injectInstanceStatic, Pair(Pair("a", 1), setOf("b")))
+      membersInjector.staticInjectMethod("set")
+          .invoke(null, injectInstanceStatic, setOf { _: List<String> -> listOf("d") })
+
+      assertThat(injectInstanceConstructor).isEqualTo(injectInstanceStatic)
+      assertThat(injectInstanceConstructor).isNotSameInstanceAs(injectInstanceStatic)
+    }
+  }
+
+  @Test fun `a factory class is generated for a field injection with Lazy and Provider`() {
+    /*
+package com.squareup.test;
+
+import dagger.Lazy;
+import dagger.MembersInjector;
+import dagger.internal.DoubleCheck;
+import dagger.internal.InjectedFieldSignature;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_MembersInjector implements MembersInjector<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<String> stringProvider2;
+
+  private final Provider<List<String>> stringListProvider;
+
+  private final Provider<String> stringProvider3;
+
+  public InjectClass_MembersInjector(Provider<String> stringProvider,
+      Provider<String> stringProvider2, Provider<List<String>> stringListProvider,
+      Provider<String> stringProvider3) {
+    this.stringProvider = stringProvider;
+    this.stringProvider2 = stringProvider2;
+    this.stringListProvider = stringListProvider;
+    this.stringProvider3 = stringProvider3;
+  }
+
+  public static MembersInjector<InjectClass> create(Provider<String> stringProvider,
+      Provider<String> stringProvider2, Provider<List<String>> stringListProvider,
+      Provider<String> stringProvider3) {
+    return new InjectClass_MembersInjector(stringProvider, stringProvider2, stringListProvider, stringProvider3);}
+
+  @Override
+  public void injectMembers(InjectClass instance) {
+    injectString(instance, stringProvider.get());
+    injectStringProvider(instance, stringProvider2);
+    injectStringListProvider(instance, stringListProvider);
+    injectLazyString(instance, DoubleCheck.lazy(stringProvider3));
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.string")
+  public static void injectString(InjectClass instance, String string) {
+    instance.string = string;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.stringProvider")
+  public static void injectStringProvider(InjectClass instance, Provider<String> stringProvider) {
+    instance.stringProvider = stringProvider;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.stringListProvider")
+  public static void injectStringListProvider(InjectClass instance,
+      Provider<List<String>> stringListProvider) {
+    instance.stringListProvider = stringListProvider;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.lazyString")
+  public static void injectLazyString(InjectClass instance, Lazy<String> lazyString) {
+    instance.lazyString = lazyString;
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Lazy
+        import javax.inject.Inject
+        import javax.inject.Provider
+        
+        class InjectClass {
+          @Inject lateinit var string: String
+          @Inject lateinit var stringProvider: Provider<String>
+          @Inject lateinit var stringListProvider: Provider<List<String>>
+          @Inject lateinit var lazyString: Lazy<String>
+          
+          override fun equals(other: Any?): Boolean {
+            return toString() == other.toString()
+          }
+          override fun toString(): String {
+           return string + stringProvider.get() + 
+               stringListProvider.get()[0] + lazyString.get()
+          }
+        }
+    """
+    ) {
+      val membersInjector = injectClass.membersInjector()
+
+      val constructor = membersInjector.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(
+              Provider::class.java, Provider::class.java, Provider::class.java, Provider::class.java
+          )
+
+      val membersInjectorInstance = constructor
+          .newInstance(
+              Provider { "a" }, Provider { "b" }, Provider { listOf("c") },
+              Provider { "d" }
+          )
+          as MembersInjector<Any>
+
+      val injectInstanceConstructor = injectClass.newInstance()
+      membersInjectorInstance.injectMembers(injectInstanceConstructor)
+
+      val injectInstanceStatic = injectClass.newInstance()
+
+      membersInjector.staticInjectMethod("string")
+          .invoke(null, injectInstanceStatic, "a")
+      membersInjector.staticInjectMethod("stringProvider")
+          .invoke(null, injectInstanceStatic, Provider { "b" })
+      membersInjector.staticInjectMethod("stringListProvider")
+          .invoke(null, injectInstanceStatic, Provider { listOf("c") })
+      membersInjector.staticInjectMethod("lazyString")
+          .invoke(null, injectInstanceStatic, Lazy { "d" })
+
+      assertThat(injectInstanceConstructor).isEqualTo(injectInstanceStatic)
+      assertThat(injectInstanceConstructor).isNotSameInstanceAs(injectInstanceStatic)
+    }
+  }
+
+  @Test fun `a factory class is generated for a field injection with star imports`() {
+    /*
+package com.squareup.test;
+
+import dagger.MembersInjector;
+import dagger.internal.InjectedFieldSignature;
+import error.NonExistentClass;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_MembersInjector implements MembersInjector<InjectClass> {
+  private final Provider<NonExistentClass> fileProvider;
+
+  private final Provider<NonExistentClass> pathProvider;
+
+  public InjectClass_MembersInjector(Provider<NonExistentClass> fileProvider,
+      Provider<NonExistentClass> pathProvider) {
+    this.fileProvider = fileProvider;
+    this.pathProvider = pathProvider;
+  }
+
+  public static MembersInjector<InjectClass> create(Provider<NonExistentClass> fileProvider,
+      Provider<NonExistentClass> pathProvider) {
+    return new InjectClass_MembersInjector(fileProvider, pathProvider);}
+
+  @Override
+  public void injectMembers(InjectClass instance) {
+    injectFile(instance, fileProvider.get());
+    injectPath(instance, pathProvider.get());
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.file")
+  public static void injectFile(InjectClass instance, NonExistentClass file) {
+    instance.file = file;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.InjectClass.path")
+  public static void injectPath(InjectClass instance, NonExistentClass path) {
+    instance.path = path;
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import java.io.*
+        import java.nio.file.*
+        import javax.inject.*
+        
+        class InjectClass {
+          @Inject lateinit var file: File
+          @Inject lateinit var path: Path
+          
+          override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+        
+            other as InjectClass
+        
+            if (file != other.file) return false
+            if (path != other.path) return false
+        
+            return true
+          }
+        
+          override fun hashCode(): Int {
+            var result = file.hashCode()
+            result = 31 * result + path.hashCode()
+            return result
+          }
+        }
+    """
+    ) {
+      val membersInjector = injectClass.membersInjector()
+
+      val constructor = membersInjector.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+
+      val membersInjectorInstance = constructor
+          .newInstance(Provider { File("") }, Provider { File("").toPath() })
+          as MembersInjector<Any>
+
+      val injectInstanceConstructor = injectClass.newInstance()
+      membersInjectorInstance.injectMembers(injectInstanceConstructor)
+
+      val injectInstanceStatic = injectClass.newInstance()
+
+      membersInjector.staticInjectMethod("file")
+          .invoke(null, injectInstanceStatic, File(""))
+      membersInjector.staticInjectMethod("path")
+          .invoke(null, injectInstanceStatic, File("").toPath())
+
+      assertThat(injectInstanceConstructor).isEqualTo(injectInstanceStatic)
+      assertThat(injectInstanceConstructor).isNotSameInstanceAs(injectInstanceStatic)
+    }
+  }
+
+  @Test fun `a factory class is generated for a field injection inner class`() {
+    /*
+package com.squareup.test;
+
+import dagger.MembersInjector;
+import dagger.internal.InjectedFieldSignature;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class OuterClass_InjectClass_MembersInjector implements MembersInjector<OuterClass.InjectClass> {
+  private final Provider<String> stringProvider;
+
+  private final Provider<CharSequence> charSequenceProvider;
+
+  private final Provider<List<String>> listProvider;
+
+  public OuterClass_InjectClass_MembersInjector(Provider<String> stringProvider,
+      Provider<CharSequence> charSequenceProvider, Provider<List<String>> listProvider) {
+    this.stringProvider = stringProvider;
+    this.charSequenceProvider = charSequenceProvider;
+    this.listProvider = listProvider;
+  }
+
+  public static MembersInjector<OuterClass.InjectClass> create(Provider<String> stringProvider,
+      Provider<CharSequence> charSequenceProvider, Provider<List<String>> listProvider) {
+    return new OuterClass_InjectClass_MembersInjector(stringProvider, charSequenceProvider, listProvider);}
+
+  @Override
+  public void injectMembers(OuterClass.InjectClass instance) {
+    injectString(instance, stringProvider.get());
+    injectCharSequence(instance, charSequenceProvider.get());
+    injectList(instance, listProvider.get());
+  }
+
+  @InjectedFieldSignature("com.squareup.test.OuterClass.InjectClass.string")
+  public static void injectString(OuterClass.InjectClass instance, String string) {
+    instance.string = string;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.OuterClass.InjectClass.charSequence")
+  public static void injectCharSequence(OuterClass.InjectClass instance,
+      CharSequence charSequence) {
+    instance.charSequence = charSequence;
+  }
+
+  @InjectedFieldSignature("com.squareup.test.OuterClass.InjectClass.list")
+  public static void injectList(OuterClass.InjectClass instance, List<String> list) {
+    instance.list = list;
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        class OuterClass {
+          class InjectClass {
+            @Inject lateinit var string: String
+            @Inject lateinit var charSequence: CharSequence
+            @Inject lateinit var list: List<String>
+            
+            override fun equals(other: Any?): Boolean {
+              if (this === other) return true
+              if (javaClass != other?.javaClass) return false
+          
+              other as InjectClass
+          
+              if (string != other.string) return false
+              if (charSequence != other.charSequence) return false
+              if (list != other.list) return false
+          
+              return true
+            }
+          
+            override fun hashCode(): Int {
+              var result = string.hashCode()
+              result = 31 * result + charSequence.hashCode()
+              result = 31 * result + list.hashCode()
+              return result
+            }
+          }
+        }
+    """
+    ) {
+      val injectClass = classLoader.loadClass("com.squareup.test.OuterClass\$InjectClass")
+      val membersInjector = injectClass.membersInjector()
+
+      val constructor = membersInjector.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java, Provider::class.java)
+
+      val membersInjectorInstance = constructor
+          .newInstance(Provider { "a" }, Provider<CharSequence> { "b" }, Provider { listOf("c") })
+          as MembersInjector<Any>
+
+      val injectInstanceConstructor = injectClass.newInstance()
+      membersInjectorInstance.injectMembers(injectInstanceConstructor)
+
+      val injectInstanceStatic = injectClass.newInstance()
+
+      membersInjector.staticInjectMethod("string")
+          .invoke(null, injectInstanceStatic, "a")
+      membersInjector.staticInjectMethod("charSequence")
+          .invoke(null, injectInstanceStatic, "b" as CharSequence)
+      membersInjector.staticInjectMethod("list")
+          .invoke(null, injectInstanceStatic, listOf("c"))
+
+      assertThat(injectInstanceConstructor).isEqualTo(injectInstanceStatic)
+      assertThat(injectInstanceConstructor).isNotSameInstanceAs(injectInstanceStatic)
+    }
+  }
+
+  @OptIn(ExperimentalStdlibApi::class)
+  private fun Class<*>.staticInjectMethod(memberName: String): Method {
+    // We can't check the @InjectedFieldSignature annotation unfortunately, because it has class
+    // retention.
+    return declaredMethods
+        .filter { it.isStatic }
+        .single { it.name == "inject${memberName.capitalize(US)}" }
+  }
+
+  private fun compile(
+    source: String,
+    block: Result.() -> Unit = { }
+  ): Result = com.squareup.anvil.compiler.compile(
+      source = source,
+      enableDaggerAnnotationProcessor = useDagger,
+      generateDaggerFactories = !useDagger,
+      block = block
+  )
+}
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt
new file mode 100644
index 00000000..12a7f621
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt
@@ -0,0 +1,1970 @@
+package com.squareup.anvil.compiler.dagger
+
+import com.google.common.truth.Truth.assertThat
+import com.squareup.anvil.compiler.daggerModule1
+import com.squareup.anvil.compiler.innerModule
+import com.squareup.anvil.compiler.isStatic
+import com.squareup.anvil.compiler.moduleFactoryClass
+import com.tschuchort.compiletesting.KotlinCompilation.Result
+import dagger.Lazy
+import dagger.internal.Factory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+import java.io.File
+import javax.inject.Provider
+
+@Suppress("UNCHECKED_CAST")
+@RunWith(Parameterized::class)
+class ProvidesMethodFactoryGeneratorTest(
+  private val useDagger: Boolean
+) {
+
+  companion object {
+    @Parameters(name = "Use Dagger: {0}")
+    @JvmStatic fun useDagger(): Collection<Any> {
+      return listOf(true, false)
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module);
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideStringFactory(module);
+  }
+
+  public static String provideString(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        class DaggerModule1 {
+          @dagger.Provides fun provideString(): String = "abc"
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provided Factory and avoids ambiguous import`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideFactoryFactory implements Factory<com.squareup.anvil.compiler.dagger.Factory> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideFactoryFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public com.squareup.anvil.compiler.dagger.Factory get() {
+    return provideFactory(module);
+  }
+
+  public static DaggerModule1_ProvideFactoryFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideFactoryFactory(module);
+  }
+
+  public static com.squareup.anvil.compiler.dagger.Factory provideFactory(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideFactory(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+    compile(
+        """
+        package com.squareup.test
+
+        import com.squareup.anvil.compiler.dagger.Factory
+        
+        @dagger.Module
+        class DaggerModule1 {
+          @dagger.Provides fun provideFactory(): Factory = Factory
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideFactory")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedFactory = staticMethods.single { it.name == "provideFactory" }
+          .invoke(null, module) as Any
+
+      assertThat((factoryInstance as Factory<*>).get()).isSameInstanceAs(providedFactory)
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with imports`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module);
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideStringFactory(module);
+  }
+
+  public static String provideString(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(): String = "abc"
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with imports and fully qualified return type`() { // ktlint-disable max-line-length
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.io.File;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideFileFactory implements Factory<File> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideFileFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public File get() {
+    return provideFile(module);
+  }
+
+  public static DaggerModule1_ProvideFileFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideFileFactory(module);
+  }
+
+  public static File provideFile(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideFile(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideFile(): java.io.File = java.io.File("")
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideFile")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedFile = staticMethods.single { it.name == "provideFile" }
+          .invoke(null, module) as File
+
+      assertThat(providedFile).isEqualTo(File(""))
+      assertThat((factoryInstance as Factory<File>).get()).isEqualTo(File(""))
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with star imports`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.io.File;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideFileFactory implements Factory<File> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideFileFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public File get() {
+    return provideFile(module);
+  }
+
+  public static DaggerModule1_ProvideFileFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideFileFactory(module);
+  }
+
+  public static File provideFile(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideFile(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.*
+        import java.io.File
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideFile(): File = File("")
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideFile")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedFile = staticMethods.single { it.name == "provideFile" }
+          .invoke(null, module) as File
+
+      assertThat(providedFile).isEqualTo(File(""))
+      assertThat((factoryInstance as Factory<File>).get()).isEqualTo(File(""))
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with generic type`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringListFactory implements Factory<List<String>> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideStringListFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public List<String> get() {
+    return provideStringList(module);
+  }
+
+  public static DaggerModule1_ProvideStringListFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideStringListFactory(module);
+  }
+
+  public static List<String> provideStringList(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideStringList(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        class DaggerModule1 {
+          @dagger.Provides fun provideStringList(): List<String> = listOf("abc")
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideStringList")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+      assertThat(staticMethods).hasSize(2)
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideStringList" }
+          .invoke(null, module) as List<String>
+
+      assertThat(providedString).containsExactly("abc")
+      assertThat((factoryInstance as Factory<List<String>>).get()).containsExactly("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with advanced generics`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import javax.annotation.Generated;
+import kotlin.Pair;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvidePairFactory implements Factory<Pair<Pair<String, Integer>, List<String>>> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvidePairFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public Pair<Pair<String, Integer>, List<String>> get() {
+    return providePair(module);
+  }
+
+  public static DaggerModule1_ProvidePairFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvidePairFactory(module);
+  }
+
+  public static Pair<Pair<String, Integer>, List<String>> providePair(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.providePair(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.*
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun providePair(): Pair<Pair<String, Int>, List<String>> = Pair(Pair("", 1), listOf(""))
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("providePair")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "providePair" }
+          .invoke(null, module) as Pair<Pair<String, Int>, List<String>>
+
+      val expected = Pair(Pair("", 1), listOf(""))
+      assertThat(providedString).isEqualTo(expected)
+      assertThat((factoryInstance as Factory<Pair<Pair<String, Int>, List<String>>>).get())
+          .isEqualTo(expected)
+    }
+  }
+
+  @Test fun `two factory classes are generated for two provider methods`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module);
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideStringFactory(module);
+  }
+
+  public static String provideString(DaggerModule1 instance) {
+    return Preconditions.checkNotNull(instance.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideIntFactory implements Factory<Integer> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideIntFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  public Integer get() {
+    return provideInt(module);
+  }
+
+  public static DaggerModule1_ProvideIntFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideIntFactory(module);
+  }
+
+  public static int provideInt(DaggerModule1 instance) {
+    return instance.provideInt();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        class DaggerModule1 {
+          @dagger.Provides fun provideString(): String = "abc"
+          @dagger.Provides fun provideInt(): Int = 5
+        }
+    """
+    ) {
+      fun <T> verifyClassGenerated(
+        providerMethodName: String,
+        expectedResult: T
+      ) {
+        val factoryClass = daggerModule1.moduleFactoryClass(providerMethodName)
+
+        val constructor = factoryClass.declaredConstructors.single()
+        assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+        val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+        assertThat(staticMethods).hasSize(2)
+
+        val module = daggerModule1.newInstance()
+
+        val factoryInstance = staticMethods.single { it.name == "create" }
+            .invoke(null, module)
+        assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+        val providedString = staticMethods.single { it.name == providerMethodName }
+            .invoke(null, module) as T
+
+        assertThat(providedString).isEqualTo(expectedResult)
+        assertThat((factoryInstance as Factory<T>).get()).isEqualTo(expectedResult)
+      }
+
+      verifyClassGenerated("provideString", "abc")
+      verifyClassGenerated("provideInt", 5)
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method in an object`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  @Override
+  public String get() {
+    return provideString();
+  }
+
+  public static DaggerModule1_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static String provideString() {
+    return Preconditions.checkNotNull(DaggerModule1.INSTANCE.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final DaggerModule1_ProvideStringFactory INSTANCE = new DaggerModule1_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        object DaggerModule1 {
+          @dagger.Provides fun provideString(): String = "abc"
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with parameters`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module, Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    this.module = module;
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module, param1Provider.get(), param2Provider.get());
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module,
+      Provider<String> param1Provider, Provider<CharSequence> param2Provider) {
+    return new DaggerModule1_ProvideStringFactory(module, param1Provider, param2Provider);
+  }
+
+  public static String provideString(DaggerModule1 instance, String param1, CharSequence param2) {
+    return Preconditions.checkNotNull(instance.provideString(param1, param2), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: String, 
+            param2: CharSequence 
+          ): String = param1 + param2
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(daggerModule1, Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module, Provider { "a" }, Provider<CharSequence> { "b" })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module, "a", "b" as CharSequence) as String
+
+      assertThat(providedString).isEqualTo("ab")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("ab")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with provider parameters`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  private final Provider<List<String>> param3Provider;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module, Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider, Provider<List<String>> param3Provider) {
+    this.module = module;
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+    this.param3Provider = param3Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module, param1Provider.get(), param2Provider, param3Provider);
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module,
+      Provider<String> param1Provider, Provider<CharSequence> param2Provider,
+      Provider<List<String>> param3Provider) {
+    return new DaggerModule1_ProvideStringFactory(module, param1Provider, param2Provider, param3Provider);
+  }
+
+  public static String provideString(DaggerModule1 instance, String param1,
+      Provider<CharSequence> param2, Provider<List<String>> param3) {
+    return Preconditions.checkNotNull(instance.provideString(param1, param2, param3), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        import javax.inject.Provider
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: String, 
+            param2: Provider<CharSequence>, 
+            param3: Provider<List<String>> 
+          ): String = param1 + param2.get() + param3.get()[0]
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(
+              daggerModule1, Provider::class.java, Provider::class.java, Provider::class.java
+          )
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module, Provider { "a" }, Provider { "b" }, Provider { listOf("c") })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module, "a", Provider<CharSequence> { "b" }, Provider { listOf("c") })
+          as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with lazy parameters`() {
+    /*
+package com.squareup.test;
+
+import dagger.Lazy;
+import dagger.internal.DoubleCheck;
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  private final Provider<List<String>> param3Provider;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module, Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider, Provider<List<String>> param3Provider) {
+    this.module = module;
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+    this.param3Provider = param3Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module, param1Provider.get(), DoubleCheck.lazy(param2Provider), DoubleCheck.lazy(param3Provider));
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module,
+      Provider<String> param1Provider, Provider<CharSequence> param2Provider,
+      Provider<List<String>> param3Provider) {
+    return new DaggerModule1_ProvideStringFactory(module, param1Provider, param2Provider, param3Provider);
+  }
+
+  public static String provideString(DaggerModule1 instance, String param1,
+      Lazy<CharSequence> param2, Lazy<List<String>> param3) {
+    return Preconditions.checkNotNull(instance.provideString(param1, param2, param3), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Lazy
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: String, 
+            param2: Lazy<CharSequence>, 
+            param3: Lazy<List<String>> 
+          ): String = param1 + param2.get() + param3.get()[0]
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(
+              daggerModule1, Provider::class.java, Provider::class.java, Provider::class.java
+          )
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module, Provider { "a" }, Provider { "b" }, Provider { listOf("c") })
+          as Factory<String>
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module, "a", Lazy<CharSequence> { "b" }, Lazy { listOf("c") })
+          as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat(factoryInstance.get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with generic parameters`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import kotlin.Pair;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  private final Provider<List<String>> param1Provider;
+
+  private final Provider<Pair<Pair<String, Integer>, ? extends List<String>>> param2Provider;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module,
+      Provider<List<String>> param1Provider,
+      Provider<Pair<Pair<String, Integer>, ? extends List<String>>> param2Provider) {
+    this.module = module;
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(module, param1Provider.get(), param2Provider.get());
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module,
+      Provider<List<String>> param1Provider,
+      Provider<Pair<Pair<String, Integer>, ? extends List<String>>> param2Provider) {
+    return new DaggerModule1_ProvideStringFactory(module, param1Provider, param2Provider);
+  }
+
+  public static String provideString(DaggerModule1 instance, List<String> param1,
+      Pair<Pair<String, Integer>, ? extends List<String>> param2) {
+    return Preconditions.checkNotNull(instance.provideString(param1, param2), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: List<String>, 
+            param2: Pair<Pair<String, Int>, List<String>> 
+          ): String = param1[0] + param2.first.first + param2.second[0]
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(daggerModule1, Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module, Provider { listOf("a") },
+              Provider { Pair(Pair("b", 1), listOf("c")) }
+          )
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module, listOf("a"), Pair(Pair("b", 1), listOf("c"))) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with parameters in an object`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  public DaggerModule1_ProvideStringFactory(Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(param1Provider.get(), param2Provider.get());
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    return new DaggerModule1_ProvideStringFactory(param1Provider, param2Provider);
+  }
+
+  public static String provideString(String param1, CharSequence param2) {
+    return Preconditions.checkNotNull(DaggerModule1.INSTANCE.provideString(param1, param2), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: String, 
+            param2: CharSequence 
+          ): String = param1 + param2
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { "a" }, Provider<CharSequence> { "b" })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, "a", "b" as CharSequence) as String
+
+      assertThat(providedString).isEqualTo("ab")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("ab")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method with parameters in a companion object`() { // ktlint-disable max-line-length
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_Companion_ProvideStringFactory implements Factory<String> {
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  public DaggerModule1_Companion_ProvideStringFactory(Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+  }
+
+  @Override
+  public String get() {
+    return provideString(param1Provider.get(), param2Provider.get());
+  }
+
+  public static DaggerModule1_Companion_ProvideStringFactory create(Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    return new DaggerModule1_Companion_ProvideStringFactory(param1Provider, param2Provider);
+  }
+
+  public static String provideString(String param1, CharSequence param2) {
+    return Preconditions.checkNotNull(DaggerModule1.Companion.provideString(param1, param2), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        abstract class DaggerModule1 {
+          @dagger.Binds abstract fun bindString(string: String): CharSequence
+          
+          companion object {
+            @Provides fun provideString(
+              @Named("abc") param1: String, 
+              param2: CharSequence 
+            ): String = param1 + param2
+          }
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString", companion = true)
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, Provider { "a" }, Provider<CharSequence> { "b" })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, "a", "b" as CharSequence) as String
+
+      assertThat(providedString).isEqualTo("ab")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("ab")
+    }
+  }
+
+  @Test fun `a factory class is generated for a nullable provider method`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import org.jetbrains.annotations.Nullable;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module) {
+    this.module = module;
+  }
+
+  @Override
+  @Nullable
+  public String get() {
+    return provideString(module);
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module) {
+    return new DaggerModule1_ProvideStringFactory(module);
+  }
+
+  @Nullable
+  public static String provideString(DaggerModule1 instance) {
+    return instance.provideString();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(): String? = null
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(daggerModule1)
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module) as? String
+
+      assertThat(providedString).isNull()
+      assertThat((factoryInstance as Factory<String>).get()).isNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for a nullable provider method in an object`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import org.jetbrains.annotations.Nullable;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  @Override
+  @Nullable
+  public String get() {
+    return provideString();
+  }
+
+  public static DaggerModule1_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  @Nullable
+  public static String provideString() {
+    return DaggerModule1.INSTANCE.provideString();
+  }
+
+  private static final class InstanceHolder {
+    private static final DaggerModule1_ProvideStringFactory INSTANCE = new DaggerModule1_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun provideString(): String? = null
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as? String
+
+      assertThat(providedString).isNull()
+      assertThat((factoryInstance as Factory<String>).get()).isNull()
+    }
+  }
+
+  @Test fun `a factory class is generated for a nullable provider method with nullable parameters`() { // ktlint-disable max-line-length
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import org.jetbrains.annotations.Nullable;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  private final DaggerModule1 module;
+
+  private final Provider<String> param1Provider;
+
+  private final Provider<CharSequence> param2Provider;
+
+  public DaggerModule1_ProvideStringFactory(DaggerModule1 module, Provider<String> param1Provider,
+      Provider<CharSequence> param2Provider) {
+    this.module = module;
+    this.param1Provider = param1Provider;
+    this.param2Provider = param2Provider;
+  }
+
+  @Override
+  @Nullable
+  public String get() {
+    return provideString(module, param1Provider.get(), param2Provider.get());
+  }
+
+  public static DaggerModule1_ProvideStringFactory create(DaggerModule1 module,
+      Provider<String> param1Provider, Provider<CharSequence> param2Provider) {
+    return new DaggerModule1_ProvideStringFactory(module, param1Provider, param2Provider);
+  }
+
+  @Nullable
+  public static String provideString(DaggerModule1 instance, String param1, CharSequence param2) {
+    return instance.provideString(param1, param2);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import javax.inject.Named
+        
+        @Module
+        class DaggerModule1 {
+          @Provides fun provideString(
+            @Named("abc") param1: String?, 
+            param2: CharSequence? 
+          ): String? {
+            check(param1 == null)
+            check(param2 == null)
+            return null
+          }
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+          .containsExactly(daggerModule1, Provider::class.java, Provider::class.java)
+          .inOrder()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val module = daggerModule1.newInstance()
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null, module, Provider { null }, Provider { null })
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null, module, null, null) as? String
+
+      assertThat(providedString).isNull()
+      assertThat((factoryInstance as Factory<String>).get()).isNull()
+    }
+  }
+
+  @Test fun `no factory class is generated for a binding method in an abstract class`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        abstract class DaggerModule1 {
+          @dagger.Binds abstract fun bindString(string: String): CharSequence
+        }
+    """
+    ) {
+      if (useDagger) {
+        assertThat(sourcesGeneratedByAnnotationProcessor).isEmpty()
+      }
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method in a companion object`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_Companion_ProvideStringFactory implements Factory<String> {
+  @Override
+  public String get() {
+    return provideString();
+  }
+
+  public static DaggerModule1_Companion_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static String provideString() {
+    return Preconditions.checkNotNull(DaggerModule1.Companion.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final DaggerModule1_Companion_ProvideStringFactory INSTANCE = new DaggerModule1_Companion_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        abstract class DaggerModule1 {
+          @dagger.Binds abstract fun bindString(string: String): CharSequence
+          
+          companion object {
+            @dagger.Provides fun provideString(): String = "abc"          
+          }
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString", companion = true)
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a provider method in an inner module`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class ComponentInterface_InnerModule_ProvideStringFactory implements Factory<String> {
+  @Override
+  public String get() {
+    return provideString();
+  }
+
+  public static ComponentInterface_InnerModule_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static String provideString() {
+    return Preconditions.checkNotNull(ComponentInterface.InnerModule.INSTANCE.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final ComponentInterface_InnerModule_ProvideStringFactory INSTANCE = new ComponentInterface_InnerModule_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        interface ComponentInterface {
+          @dagger.Module
+          object InnerModule {
+            @dagger.Provides fun provideString(): String = "abc"          
+          }
+        }
+        
+    """
+    ) {
+      val factoryClass = innerModule.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test
+  fun `a factory class is generated for a provider method in a companion object in an inner module`() { // ktlint-disable max-line-length
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class ComponentInterface_InnerModule_Companion_ProvideStringFactory implements Factory<String> {
+  @Override
+  public String get() {
+    return provideString();
+  }
+
+  public static ComponentInterface_InnerModule_Companion_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static String provideString() {
+    return Preconditions.checkNotNull(ComponentInterface.InnerModule.Companion.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final ComponentInterface_InnerModule_Companion_ProvideStringFactory INSTANCE = new ComponentInterface_InnerModule_Companion_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        interface ComponentInterface {
+          @dagger.Module
+          abstract class InnerModule {
+            @dagger.Binds abstract fun bindString(string: String): CharSequence
+            
+            companion object {
+              @dagger.Provides fun provideString(): String = "abc"          
+            }
+          }
+        }
+        
+    """
+    ) {
+      val factoryClass = innerModule.moduleFactoryClass("provideString", companion = true)
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `no factory class is generated for multibindings`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        abstract class DaggerModule1 {
+          @dagger.Binds @dagger.multibindings.IntoSet abstract fun bindString(string: String): CharSequence
+        }
+    """
+    ) {
+      if (useDagger) {
+        assertThat(sourcesGeneratedByAnnotationProcessor).isEmpty()
+      }
+    }
+  }
+
+  @Test fun `a factory class is generated for multibindings provider method`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<String> {
+  @Override
+  public String get() {
+    return provideString();
+  }
+
+  public static DaggerModule1_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static String provideString() {
+    return Preconditions.checkNotNull(DaggerModule1.INSTANCE.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final DaggerModule1_ProvideStringFactory INSTANCE = new DaggerModule1_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        object DaggerModule1 {
+          @dagger.Provides @dagger.multibindings.IntoSet fun provideString(): String = "abc"
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedString = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as String
+
+      assertThat(providedString).isEqualTo("abc")
+      assertThat((factoryInstance as Factory<String>).get()).isEqualTo("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for multibindings provider method elements into set`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.Set;
+import javax.annotation.Generated;
+
+@Generated(
+  value = "dagger.internal.codegen.ComponentProcessor",
+  comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+  "unchecked",
+  "rawtypes"
+})
+public final class DaggerModule1_ProvideStringFactory implements Factory<Set<String>> {
+  @Override
+  public Set<String> get() {
+    return provideString();
+  }
+
+  public static DaggerModule1_ProvideStringFactory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static Set<String> provideString() {
+    return Preconditions.checkNotNull(DaggerModule1.INSTANCE.provideString(), "Cannot return null from a non-@Nullable @Provides method");
+  }
+
+  private static final class InstanceHolder {
+    private static final DaggerModule1_ProvideStringFactory INSTANCE = new DaggerModule1_ProvideStringFactory();
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        @dagger.Module
+        object DaggerModule1 {
+          @dagger.Provides @dagger.multibindings.ElementsIntoSet fun provideString(): Set<String> = setOf("abc")
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideString")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val factoryInstance = staticMethods.single { it.name == "create" }
+          .invoke(null)
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedStringSet = staticMethods.single { it.name == "provideString" }
+          .invoke(null) as Set<String>
+
+      assertThat(providedStringSet).containsExactly("abc")
+      assertThat((factoryInstance as Factory<Set<String>>).get()).containsExactly("abc")
+    }
+  }
+
+  @Test fun `a factory class is generated for a multibindings provided function with a typealias`() { // ktlint-disable max-line-length
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import dagger.internal.Preconditions;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+import kotlin.jvm.functions.Function1;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class DaggerModule1_ProvideFunctionFactory implements Factory<Set<Function1<List<String>, List<String>>>> {
+  private final Provider<String> stringProvider;
+
+  public DaggerModule1_ProvideFunctionFactory(Provider<String> stringProvider) {
+    this.stringProvider = stringProvider;
+  }
+
+  @Override
+  public Set<Function1<List<String>, List<String>>> get() {
+    return provideFunction(stringProvider.get());
+  }
+
+  public static DaggerModule1_ProvideFunctionFactory create(Provider<String> stringProvider) {
+    return new DaggerModule1_ProvideFunctionFactory(stringProvider);
+  }
+
+  public static Set<Function1<List<String>, List<String>>> provideFunction(String string) {
+    return Preconditions.checkNotNull(DaggerModule1.INSTANCE.provideFunction(string), "Cannot return null from a non-@Nullable @Provides method");
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import dagger.multibindings.ElementsIntoSet
+        
+        typealias StringList = List<String>
+        
+        @Module
+        object DaggerModule1 {
+          @Provides @ElementsIntoSet fun provideFunction(
+            string: String
+          ): @JvmSuppressWildcards Set<(StringList) -> StringList> {
+            return setOf { list -> listOf(string) }
+          }
+        }
+    """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideFunction")
+
+      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 { "abc" })
+          as Factory<Set<(List<String>) -> List<String>>>
+      assertThat(factoryInstance::class.java).isEqualTo(factoryClass)
+
+      val providedStringSet = staticMethods.single { it.name == "provideFunction" }
+          .invoke(null, "abc") as Set<(List<String>) -> List<String>>
+
+      assertThat(providedStringSet.single().invoke(emptyList())).containsExactly("abc")
+      assertThat(factoryInstance.get().single().invoke(emptyList())).containsExactly("abc")
+    }
+  }
+
+  private fun compile(
+    source: String,
+    block: Result.() -> Unit = { }
+  ): Result = com.squareup.anvil.compiler.compile(
+      source,
+      enableDaggerAnnotationProcessor = useDagger,
+      generateDaggerFactories = !useDagger,
+      block = block
+  )
+}
diff --git a/integration-tests/library/build.gradle b/integration-tests/library/build.gradle
index 1cf43dc7..1a088d5c 100644
--- a/integration-tests/library/build.gradle
+++ b/integration-tests/library/build.gradle
@@ -1,10 +1,11 @@
 apply plugin: 'org.jetbrains.kotlin.jvm'
-apply plugin: 'org.jetbrains.kotlin.kapt'
 apply plugin: 'com.squareup.anvil'
 
+anvil {
+  generateDaggerFactories = true
+}
+
 dependencies {
   api deps.dagger2.dagger
   api deps.kotlin.stdlib
-
-  kapt deps.dagger2.compiler
 }