Repository: square/anvil (kotlin)

Issue:
AssistedInject-annotated classes's factories don't account for member injection
Given this example

```kotlin
class Example @AssistedInject constructor() {
  @Inject lateinit var injected: String
}
```

The generated factory is missing a member injection call

```kotlin
public class Example_Factory(
  // private val param0: Provider<String> // <-- Missing parameter input
) : Factory<Example> {
  public override fun get(): Example {
    val instance = newInstance()
    // Example_MembersInjector.injectInjected(instance, param0.get()) // <-- Missing injection
    return instance
  }
}
```

The relevant `MembersInjector` is correctly generated, just not accounted for here.

Pull request description:
Resolves #342 and also leaves some important consolidated logic toe-holds along the way for helping with #341, #340, #339, and #343

Patch:
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
index 6824a21d5..e5dfb6609 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/AssistedInjectGenerator.kt
@@ -71,9 +71,16 @@ internal class AssistedInjectGenerator : PrivateCodeGenerator() {
     val packageName = clazz.containingKtFile.packageFqName.safePackageString()
     val className = "${clazz.generateClassName()}_Factory"
 
-    val parameters = constructor.valueParameters.mapToParameter(module)
+    val memberInjectProperties = clazz.injectedMembers(module)
+
+    val parameters = constructor.valueParameters
+      .plus(memberInjectProperties)
+      .mapToParameter(module)
     val parametersAssisted = parameters.filter { it.isAssisted }
     val parametersNotAssisted = parameters.filterNot { it.isAssisted }
+    val constructorSize = constructor.valueParameters.size
+    val constructorParameters = parameters.take(constructorSize)
+    val memberInjectParameters = parameters.drop(constructorSize)
 
     checkAssistedParametersAreDistinct(clazz, parametersAssisted)
 
@@ -119,12 +126,25 @@ internal class AssistedInjectGenerator : PrivateCodeGenerator() {
                 addParameter(parameter.name, parameter.originalTypeName)
               }
 
-              val argumentList = parameters.asArgumentList(
+              val argumentList = constructorParameters.asArgumentList(
                 asProvider = true,
                 includeModule = false
               )
 
-              addStatement("return newInstance($argumentList)")
+              if (memberInjectParameters.isEmpty()) {
+                addStatement("return newInstance($argumentList)")
+              } else {
+                val instanceName = "instance"
+                addStatement("val $instanceName = newInstance($argumentList)")
+                addMemberInjection(
+                  packageName,
+                  clazz,
+                  memberInjectParameters,
+                  memberInjectProperties,
+                  instanceName
+                )
+                addStatement("return $instanceName")
+              }
             }
             .build()
         )
@@ -161,13 +181,13 @@ internal class AssistedInjectGenerator : PrivateCodeGenerator() {
                   if (typeParameters.isNotEmpty()) {
                     addTypeVariables(typeParameters)
                   }
-                  parameters.forEach { parameter ->
+                  constructorParameters.forEach { parameter ->
                     addParameter(
                       name = parameter.name,
                       type = parameter.originalTypeName
                     )
                   }
-                  val argumentsWithoutModule = parameters.joinToString { it.name }
+                  val argumentsWithoutModule = constructorParameters.joinToString { it.name }
 
                   addStatement("return %T($argumentsWithoutModule)", classType)
                 }
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
index df69012d2..b2b29223c 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/InjectConstructorFactoryGenerator.kt
@@ -9,14 +9,11 @@ import com.squareup.anvil.compiler.codegen.PrivateCodeGenerator
 import com.squareup.anvil.compiler.codegen.asArgumentList
 import com.squareup.anvil.compiler.codegen.injectConstructor
 import com.squareup.anvil.compiler.codegen.mapToParameter
-import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
 import com.squareup.anvil.compiler.injectFqName
 import com.squareup.anvil.compiler.internal.asClassName
 import com.squareup.anvil.compiler.internal.buildFile
-import com.squareup.anvil.compiler.internal.capitalize
 import com.squareup.anvil.compiler.internal.classesAndInnerClass
 import com.squareup.anvil.compiler.internal.generateClassName
-import com.squareup.anvil.compiler.internal.hasAnnotation
 import com.squareup.anvil.compiler.internal.safePackageString
 import com.squareup.anvil.compiler.internal.typeVariableNames
 import com.squareup.kotlinpoet.ClassName
@@ -31,12 +28,9 @@ 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.lexer.KtTokens
-import org.jetbrains.kotlin.psi.KtClassBody
 import org.jetbrains.kotlin.psi.KtClassOrObject
 import org.jetbrains.kotlin.psi.KtConstructor
 import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
 import java.io.File
 
 @AutoService(CodeGenerator::class)
@@ -68,13 +62,7 @@ internal class InjectConstructorFactoryGenerator : PrivateCodeGenerator() {
     val packageName = clazz.containingKtFile.packageFqName.safePackageString()
     val className = "${clazz.generateClassName()}_Factory"
 
-    val memberInjectProperties = clazz.children
-      .asSequence()
-      .filterIsInstance<KtClassBody>()
-      .flatMap { it.properties.asSequence() }
-      .filterNot { it.visibilityModifierTypeOrDefault() == KtTokens.PRIVATE_KEYWORD }
-      .filter { it.hasAnnotation(injectFqName, module) }
-      .toList()
+    val memberInjectProperties = clazz.injectedMembers(module)
 
     val parameters = constructor.valueParameters
       .plus(memberInjectProperties)
@@ -141,27 +129,16 @@ internal class InjectConstructorFactoryGenerator : PrivateCodeGenerator() {
               if (memberInjectParameters.isEmpty()) {
                 addStatement("return newInstance($newInstanceArgumentList)")
               } else {
-                addStatement("val instance = newInstance($newInstanceArgumentList)")
-
-                val memberInjectorClassName = "${clazz.generateClassName()}_MembersInjector"
-                val memberInjectorClass = ClassName(packageName, memberInjectorClassName)
-
-                memberInjectParameters.forEachIndexed { index, parameter ->
-
-                  val property = memberInjectProperties[index]
-                  val propertyName = property.nameAsSafeName.asString()
-                  val functionName = "inject${propertyName.capitalize()}"
-
-                  val param = when {
-                    parameter.isWrappedInProvider -> parameter.name
-                    parameter.isWrappedInLazy ->
-                      "$daggerDoubleCheckFqNameString.lazy(${parameter.name})"
-                    else -> parameter.name + ".get()"
-                  }
-
-                  addStatement("%T.$functionName(instance, $param)", memberInjectorClass)
-                }
-                addStatement("return instance")
+                val instanceName = "instance"
+                addStatement("val $instanceName = newInstance($newInstanceArgumentList)")
+                addMemberInjection(
+                  packageName,
+                  clazz,
+                  memberInjectParameters,
+                  memberInjectProperties,
+                  instanceName
+                )
+                addStatement("return $instanceName")
               }
             }
             .build()
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectionUtils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectionUtils.kt
new file mode 100644
index 000000000..b61d650f6
--- /dev/null
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/MembersInjectionUtils.kt
@@ -0,0 +1,65 @@
+package com.squareup.anvil.compiler.codegen.dagger
+
+import com.squareup.anvil.compiler.codegen.Parameter
+import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
+import com.squareup.anvil.compiler.injectFqName
+import com.squareup.anvil.compiler.internal.capitalize
+import com.squareup.anvil.compiler.internal.findAnnotation
+import com.squareup.anvil.compiler.internal.generateClassName
+import com.squareup.anvil.compiler.internal.hasAnnotation
+import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FunSpec
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtClassBody
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
+
+internal fun KtProperty.isSetterInjected(module: ModuleDescriptor): Boolean {
+  return setter?.hasAnnotation(injectFqName, module) == true ||
+    findAnnotation(injectFqName, module)
+    ?.useSiteTarget
+    ?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.PROPERTY_SETTER
+}
+
+// TODO
+//  Include methods: https://github.com/square/anvil/issues/339
+//  Include superclass: https://github.com/square/anvil/issues/343
+internal fun KtClassOrObject.injectedMembers(module: ModuleDescriptor) = children
+  .asSequence()
+  .filterIsInstance<KtClassBody>()
+  .flatMap { it.properties }
+  .filterNot { it.visibilityModifierTypeOrDefault() == KtTokens.PRIVATE_KEYWORD }
+  .filter {
+    it.hasAnnotation(injectFqName, module) ||
+      it.isSetterInjected(module)
+  }
+  .toList()
+
+internal fun FunSpec.Builder.addMemberInjection(
+  packageName: String,
+  clazz: KtClassOrObject,
+  memberInjectParameters: List<Parameter>,
+  memberInjectProperties: List<KtProperty>,
+  instanceName: String
+) {
+  val memberInjectorClassName = "${clazz.generateClassName()}_MembersInjector"
+  val memberInjectorClass = ClassName(packageName, memberInjectorClassName)
+
+  memberInjectParameters.forEachIndexed { index, parameter ->
+    val property = memberInjectProperties[index]
+    val propertyName = property.nameAsSafeName.asString()
+    val functionName = "inject${propertyName.capitalize()}"
+
+    val param = when {
+      parameter.isWrappedInProvider -> parameter.name
+      parameter.isWrappedInLazy ->
+        "$daggerDoubleCheckFqNameString.lazy(${parameter.name})"
+      else -> parameter.name + ".get()"
+    }
+
+    addStatement("%T.$functionName($instanceName, $param)", memberInjectorClass)
+  }
+}
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
index 0921ea852..726a1c3fd 100644
--- 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
@@ -8,13 +8,11 @@ import com.squareup.anvil.compiler.api.createGeneratedFile
 import com.squareup.anvil.compiler.codegen.PrivateCodeGenerator
 import com.squareup.anvil.compiler.codegen.mapToParameter
 import com.squareup.anvil.compiler.daggerDoubleCheckFqNameString
-import com.squareup.anvil.compiler.injectFqName
 import com.squareup.anvil.compiler.internal.asClassName
 import com.squareup.anvil.compiler.internal.buildFile
 import com.squareup.anvil.compiler.internal.capitalize
 import com.squareup.anvil.compiler.internal.classesAndInnerClass
 import com.squareup.anvil.compiler.internal.generateClassName
-import com.squareup.anvil.compiler.internal.hasAnnotation
 import com.squareup.anvil.compiler.internal.isGenericClass
 import com.squareup.anvil.compiler.internal.isQualifier
 import com.squareup.anvil.compiler.internal.requireClassDescriptor
@@ -37,12 +35,9 @@ import dagger.MembersInjector
 import dagger.internal.InjectedFieldSignature
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
 import org.jetbrains.kotlin.descriptors.PropertyDescriptor
-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 org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
 import java.io.File
 
@@ -59,13 +54,7 @@ internal class MembersInjectorGenerator : PrivateCodeGenerator() {
     projectFiles
       .classesAndInnerClass(module)
       .forEach { clazz ->
-        val injectProperties = clazz.children
-          .asSequence()
-          .filterIsInstance<KtClassBody>()
-          .flatMap { it.properties.asSequence() }
-          .filterNot { it.visibilityModifierTypeOrDefault() == KtTokens.PRIVATE_KEYWORD }
-          .filter { it.hasAnnotation(injectFqName, module) }
-          .toList()
+        val injectProperties = clazz.injectedMembers(module)
           .ifEmpty { return@forEach }
 
         generateMembersInjectorClass(
@@ -156,7 +145,12 @@ internal class MembersInjectorGenerator : PrivateCodeGenerator() {
                       "$daggerDoubleCheckFqNameString.lazy(${parameter.name})"
                     else -> parameter.name + ".get()"
                   }
-                  addStatement("inject${propertyName.capitalize()}(instance, $parameterString)")
+                  val propertyRef = if (property.isSetterInjected(module)) {
+                    "set${propertyName.capitalize()}"
+                  } else {
+                    propertyName
+                  }
+                  addStatement("inject${propertyRef.capitalize()}(instance, $parameterString)")
                 }
               }
               .build()
@@ -184,15 +178,26 @@ internal class MembersInjectorGenerator : PrivateCodeGenerator() {
                 parameters.forEachIndexed { index, parameter ->
                   val property = injectProperties[index]
                   val propertyName = property.nameAsSafeName.asString()
+                  val isSetterInjected = property.isSetterInjected(module)
+                  val propertyRef = if (isSetterInjected) {
+                    "set${propertyName.capitalize()}"
+                  } else {
+                    propertyName
+                  }
 
                   addFunction(
-                    FunSpec.builder("inject${propertyName.capitalize()}")
+                    FunSpec.builder("inject${propertyRef.capitalize()}")
                       .jvmStatic()
-                      .addAnnotation(
-                        AnnotationSpec.builder(InjectedFieldSignature::class)
-                          .addMember("%S", property.requireFqName())
-                          .build()
-                      )
+                      .apply {
+                        // Don't add @InjectedFieldSignature when it's calling a setter method
+                        if (!isSetterInjected) {
+                          addAnnotation(
+                            AnnotationSpec.builder(InjectedFieldSignature::class)
+                              .addMember("%S", property.requireFqName())
+                              .build()
+                          )
+                        }
+                      }
                       .apply {
                         val hasQualifier = property.annotationEntries
                           .any { it.isQualifier(module) }


Test patch:
diff --git a/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/ReflectionTestUtils.kt b/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/ReflectionTestUtils.kt
index 0eee65004..f2faf8097 100644
--- a/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/ReflectionTestUtils.kt
+++ b/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/ReflectionTestUtils.kt
@@ -3,7 +3,7 @@
 package com.squareup.anvil.compiler.internal.testing
 
 import com.squareup.anvil.annotations.ExperimentalAnvilApi
-import java.lang.reflect.Executable
+import java.lang.reflect.AccessibleObject
 import java.lang.reflect.Member
 import java.lang.reflect.Modifier
 
@@ -23,7 +23,7 @@ public fun <T : Any> Class<T>.createInstance(
 ): T = declaredConstructors.single().use { it.newInstance(*initargs) } as T
 
 @ExperimentalAnvilApi
-public inline fun <T, E : Executable> E.use(block: (E) -> T): T {
+public inline fun <T, E : AccessibleObject> E.use(block: (E) -> T): T {
   // Deprecated since Java 9, but many projects still use JDK 8 for compilation.
   @Suppress("DEPRECATION")
   val original = isAccessible
diff --git a/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/TestUtils.kt b/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/TestUtils.kt
index b707885e7..e78e5bd2b 100644
--- a/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/TestUtils.kt
+++ b/compiler-utils/src/testFixtures/java/com/squareup/anvil/compiler/internal/testing/TestUtils.kt
@@ -180,6 +180,11 @@ public fun Any.invokeGet(vararg args: Any?): Any {
   return method.invoke(this, *args)
 }
 
+@ExperimentalAnvilApi
+public fun Any.getPropertyValue(name: String): Any {
+  return this::class.java.getDeclaredField(name).use { it.get(this) }
+}
+
 @Suppress("UNCHECKED_CAST")
 @ExperimentalAnvilApi
 public fun <T> Annotation.getValue(): T =
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 271e59686..713df11b1 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
@@ -70,6 +70,9 @@ internal val Result.daggerModule4: Class<*>
 internal val Result.innerModule: Class<*>
   get() = classLoader.loadClass("com.squareup.test.ComponentInterface\$InnerModule")
 
+internal val Result.nestedInjectClass: Class<*>
+  get() = classLoader.loadClass("com.squareup.test.ParentClass\$NestedInjectClass")
+
 internal val Result.injectClass: Class<*>
   get() = classLoader.loadClass("com.squareup.test.InjectClass")
 
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
index a8e00c7cc..6cb89ade6 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/AssistedFactoryGeneratorTest.kt
@@ -8,6 +8,7 @@ import com.squareup.anvil.compiler.daggerModule1
 import com.squareup.anvil.compiler.internal.testing.compileAnvil
 import com.squareup.anvil.compiler.internal.testing.createInstance
 import com.squareup.anvil.compiler.internal.testing.factoryClass
+import com.squareup.anvil.compiler.internal.testing.getPropertyValue
 import com.squareup.anvil.compiler.internal.testing.implClass
 import com.squareup.anvil.compiler.internal.testing.isStatic
 import com.squareup.anvil.compiler.internal.testing.moduleFactoryClass
@@ -49,21 +50,25 @@ import javax.inject.Provider;
 })
 public final class AssistedService_Factory {
   private final Provider<Integer> p0_52215Provider;
+  private final Provider<List<String>> p2_52215Provider;
 
-  public AssistedService_Factory(Provider<Integer> p0_52215Provider) {
+  public AssistedService_Factory(Provider<Integer> p0_52215Provider, Provider<List<String>> p2_52215Provider) {
     this.p0_52215Provider = p0_52215Provider;
+    this.p2_52215Provider = p2_52215Provider;
   }
 
   public AssistedService get(String string) {
-    return newInstance(p0_52215Provider.get(), string);
+    return newInstance(p0_52215Provider.get(), string, p2_52215Provider.get());
   }
 
-  public static AssistedService_Factory create(Provider<Integer> p0_52215Provider) {
-    return new AssistedService_Factory(p0_52215Provider);
+  public static AssistedService_Factory create(Provider<Integer> p0_52215Provider, Provider<List<String>> p2_52215Provider) {
+    return new AssistedService_Factory(p0_52215Provider, p2_52215Provider);
   }
 
-  public static AssistedService newInstance(int p0_52215, String string) {
-    return new AssistedService(p0_52215, string);
+  public static AssistedService newInstance(int p0_52215, String string, List<String> p2_52215) {
+    AssistedService instance = new AssistedService(p0_52215, string);
+    AssistedService_MembersInjector.injectMember(instance, p2_52215);
+    return instance;
   }
 }
      */
@@ -106,11 +111,14 @@ public final class AssistedServiceFactory_Impl implements AssistedServiceFactory
       import dagger.assisted.Assisted
       import dagger.assisted.AssistedFactory
       import dagger.assisted.AssistedInject
+      import javax.inject.Inject
       
       data class AssistedService @AssistedInject constructor(
         val int: Int,
         @Assisted val string: String
-      )
+      ) {
+        @Inject lateinit var member: List<String>
+      }
       
       @AssistedFactory
       interface AssistedServiceFactory {
@@ -119,7 +127,8 @@ public final class AssistedServiceFactory_Impl implements AssistedServiceFactory
       """
     ) {
       val factoryImplClass = assistedServiceFactory.implClass()
-      val generatedFactoryInstance = assistedService.factoryClass().createInstance(Provider { 5 })
+      val generatedFactoryInstance = assistedService.factoryClass()
+        .createInstance(Provider { 5 }, Provider { listOf("Hello") })
       val factoryImplInstance = factoryImplClass.createInstance(generatedFactoryInstance)
 
       val staticMethods = factoryImplClass.declaredMethods.filter { it.isStatic }
@@ -135,6 +144,7 @@ public final class AssistedServiceFactory_Impl implements AssistedServiceFactory
         .invoke(factoryImplInstance, "Hello")
 
       assertThat(assistedServiceInstance).isEqualTo(assistedService.createInstance(5, "Hello"))
+      assertThat(assistedServiceInstance.getPropertyValue("member")).isEqualTo(listOf("Hello"))
     }
   }
 
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
index 7c5fba0ed..d9c1a0cca 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
@@ -9,6 +9,7 @@ import com.squareup.anvil.compiler.internal.testing.createInstance
 import com.squareup.anvil.compiler.internal.testing.getValue
 import com.squareup.anvil.compiler.internal.testing.isStatic
 import com.squareup.anvil.compiler.internal.testing.membersInjector
+import com.squareup.anvil.compiler.nestedInjectClass
 import com.tschuchort.compiletesting.KotlinCompilation.Result
 import dagger.Lazy
 import dagger.MembersInjector
@@ -39,15 +40,18 @@ class MembersInjectorGeneratorTest(
 package com.squareup.test;
 
 import dagger.MembersInjector;
+import dagger.internal.DaggerGenerated;
 import dagger.internal.InjectedFieldSignature;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import javax.annotation.Generated;
+import javax.annotation.processing.Generated;
 import javax.inject.Named;
 import javax.inject.Provider;
 import kotlin.Pair;
 import kotlin.jvm.functions.Function1;
 
+@DaggerGenerated
 @Generated(
     value = "dagger.internal.codegen.ComponentProcessor",
     comments = "https://dagger.dev"
@@ -69,24 +73,34 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
 
   private final Provider<Set<Function1<List<String>, List<String>>>> setProvider;
 
+  private final Provider<Map<String, String>> p0Provider;
+
+  private final Provider<Map<String, Boolean>> p0Provider2;
+
   public InjectClass_MembersInjector(Provider<String> stringProvider,
       Provider<String> qualifiedStringProvider, Provider<CharSequence> charSequenceProvider,
-      Provider<List<String>> listProvider, Provider<Pair<Pair<String, Integer>,
-      ? extends Set<String>>> pairProvider, Provider<Set<Function1<List<String>, List<String>>>> setProvider) {
+      Provider<List<String>> listProvider,
+      Provider<Pair<Pair<String, Integer>, ? extends Set<String>>> pairProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider,
+      Provider<Map<String, String>> p0Provider, Provider<Map<String, Boolean>> p0Provider2) {
     this.stringProvider = stringProvider;
     this.qualifiedStringProvider = qualifiedStringProvider;
     this.charSequenceProvider = charSequenceProvider;
     this.listProvider = listProvider;
     this.pairProvider = pairProvider;
     this.setProvider = setProvider;
+    this.p0Provider = p0Provider;
+    this.p0Provider2 = p0Provider2;
   }
 
   public static MembersInjector<InjectClass> create(Provider<String> stringProvider,
       Provider<String> qualifiedStringProvider, 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, qualifiedStringProvider, charSequenceProvider, listProvider, pairProvider, setProvider);}
+      Provider<List<String>> listProvider,
+      Provider<Pair<Pair<String, Integer>, ? extends Set<String>>> pairProvider,
+      Provider<Set<Function1<List<String>, List<String>>>> setProvider,
+      Provider<Map<String, String>> p0Provider, Provider<Map<String, Boolean>> p0Provider2) {
+    return new InjectClass_MembersInjector(stringProvider, qualifiedStringProvider, charSequenceProvider, listProvider, pairProvider, setProvider, p0Provider, p0Provider2);
+  }
 
   @Override
   public void injectMembers(InjectClass instance) {
@@ -96,6 +110,8 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
     injectList(instance, listProvider.get());
     injectPair(instance, pairProvider.get());
     injectSet(instance, setProvider.get());
+    injectSetSetterAnnotated(instance, p0Provider.get());
+    injectSetSetterAnnotated2(instance, p0Provider2.get());
   }
 
   @InjectedFieldSignature("com.squareup.test.InjectClass.string")
@@ -103,8 +119,8 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
     instance.string = string;
   }
 
-  @Named("qualified")
   @InjectedFieldSignature("com.squareup.test.InjectClass.qualifiedString")
+  @Named("qualified")
   public static void injectQualifiedString(InjectClass instance, String qualifiedString) {
     instance.qualifiedString = qualifiedString;
   }
@@ -130,8 +146,16 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
       Set<Function1<List<String>, List<String>>> set) {
     instance.set = set;
   }
+
+  public static void injectSetSetterAnnotated(InjectClass instance, Map<String, String> p0) {
+    instance.setSetterAnnotated(p0);
+  }
+
+  public static void injectSetSetterAnnotated2(InjectClass instance, Map<String, Boolean> p0) {
+    instance.setSetterAnnotated2(p0);
+  }
 }
-     */
+*/
 
     compile(
       """
@@ -149,6 +173,9 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
         @Inject lateinit var list: List<String>
         @Inject lateinit var pair: Pair<Pair<String, Int>, Set<String>>
         @Inject lateinit var set: @JvmSuppressWildcards Set<(StringList) -> StringList>
+        var setterAnnotated: Map<String, String> = emptyMap()
+          @Inject set
+        @set:Inject var setterAnnotated2: Map<String, Boolean> = emptyMap()
         
         override fun equals(other: Any?): Boolean {
           if (this === other) return true
@@ -162,6 +189,8 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
           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
+          if (setterAnnotated != other.setterAnnotated) return false
+          if (setterAnnotated2 != other.setterAnnotated2) return false
       
           return true
         }
@@ -173,6 +202,8 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
           result = 31 * result + list.hashCode()
           result = 31 * result + pair.hashCode()
           result = 31 * result + set.single().invoke(emptyList())[0].hashCode()
+          result = 31 * result + setterAnnotated.hashCode()
+          result = 31 * result + setterAnnotated2.hashCode()
           return result
         }
       }
@@ -188,6 +219,8 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
           Provider::class.java,
           Provider::class.java,
           Provider::class.java,
+          Provider::class.java,
+          Provider::class.java,
           Provider::class.java
         )
 
@@ -199,7 +232,9 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
           Provider<CharSequence> { "c" },
           Provider { listOf("d") },
           Provider { Pair(Pair("e", 1), setOf("f")) },
-          Provider { setOf { _: List<String> -> listOf("g") } }
+          Provider { setOf { _: List<String> -> listOf("g") } },
+          Provider { mapOf("Hello" to "World") },
+          Provider { mapOf("Hello" to false) },
         )
         as MembersInjector<Any>
 
@@ -220,6 +255,10 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
         .invoke(null, injectInstanceStatic, Pair(Pair("e", 1), setOf("f")))
       membersInjector.staticInjectMethod("set")
         .invoke(null, injectInstanceStatic, setOf { _: List<String> -> listOf("g") })
+      membersInjector.staticInjectMethod("setSetterAnnotated")
+        .invoke(null, injectInstanceStatic, mapOf("Hello" to "World"))
+      membersInjector.staticInjectMethod("setSetterAnnotated2")
+        .invoke(null, injectInstanceStatic, mapOf("Hello" to false))
 
       assertThat(injectInstanceConstructor).isEqualTo(injectInstanceStatic)
       assertThat(injectInstanceConstructor).isNotSameInstanceAs(injectInstanceStatic)
@@ -230,6 +269,104 @@ public final class InjectClass_MembersInjector implements MembersInjector<Inject
     }
   }
 
+  @Test fun `a factory class is generated for a field injection in a nested class`() {
+    /*
+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.Named;
+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 ParentClass_NestedInjectClass_MembersInjector implements MembersInjector<InjectClass> {
+  private final Provider<String> stringProvider;
+
+  public ParentClass_NestedInjectClass_MembersInjector(Provider<String> stringProvider) {
+    this.stringProvider = stringProvider;
+  }
+
+  public static MembersInjector<InjectClass> create(Provider<String> stringProvider) {
+    return new ParentClass_NestedInjectClass_MembersInjector(stringProvider);}
+
+  @Override
+  public void injectMembers(InjectClass instance) {
+    injectString(instance, stringProvider.get());
+  }
+
+  @InjectedFieldSignature("com.squareup.test.ParentClass.NestedInjectClass.string")
+  public static void injectString(InjectClass instance, String string) {
+    instance.string = string;
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      import javax.inject.Named
+      
+      typealias StringList = List<String>
+      
+      class ParentClass {
+        class NestedInjectClass {
+          @Inject lateinit var string: String
+          
+          override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+        
+            other as NestedInjectClass
+        
+            if (string != other.string) return false
+        
+            return true
+          }
+        
+          override fun hashCode(): Int {
+            return string.hashCode()
+          }
+        }
+      }
+      """
+    ) {
+      val membersInjector = nestedInjectClass.membersInjector()
+
+      val constructor = membersInjector.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList())
+        .containsExactly(Provider::class.java)
+
+      @Suppress("RedundantLambdaArrow")
+      val membersInjectorInstance = constructor
+        .newInstance(Provider { "a" }) as MembersInjector<Any>
+
+      val injectInstanceConstructor = nestedInjectClass.createInstance()
+      membersInjectorInstance.injectMembers(injectInstanceConstructor)
+
+      val injectInstanceStatic = nestedInjectClass.createInstance()
+
+      membersInjector.staticInjectMethod("string")
+        .invoke(null, injectInstanceStatic, "a")
+
+      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;