From f6a0e5c1519507e9650b14128fc6804ce572adc1 Mon Sep 17 00:00:00 2001 From: Fedor Ihnatkevich Date: Thu, 20 Nov 2025 16:06:41 +0100 Subject: [PATCH] Don't generate AssistedFactory for unsupported variants --- .gitignore | 42 +------- .idea/kotlinc.xml | 6 -- .../impl/AssistedInjectFactoryGenerator.kt | 17 +-- core/graph/api/api/api.api | 2 +- .../yandex/yatagan/core/graph/BindingGraph.kt | 3 +- .../core/graph/impl/BindingGraphImpl.kt | 6 +- .../yatagan/testing/tests/ConditionsTest.kt | 48 +++++++++ ...d-for-unsupported-variants.golden-code.txt | 101 ++++++++++++++++++ 8 files changed, 169 insertions(+), 56 deletions(-) delete mode 100644 .idea/kotlinc.xml create mode 100644 testing/tests/src/test/resources/golden/ConditionsTest/issue-209-assisted-factory-generated-for-unsupported-variants.golden-code.txt diff --git a/.gitignore b/.gitignore index 12ce845e..4dcabb7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,8 @@ -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -# From: https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore - # User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/misc.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. -.idea/artifacts -.idea/compiler.xml -.idea/jarRepositories.xml -.idea/modules.xml -.idea/*.iml -.idea/modules -*.iml -*.ipr +.idea/ +!.idea/codeStyles +!.idea/inspectionProfiles +!.idea/vcs.xml # Build dirs /build/ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 148fdd24..00000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/codegen/impl/src/main/kotlin/com/yandex/yatagan/codegen/impl/AssistedInjectFactoryGenerator.kt b/codegen/impl/src/main/kotlin/com/yandex/yatagan/codegen/impl/AssistedInjectFactoryGenerator.kt index c185e3d0..edecbf97 100644 --- a/codegen/impl/src/main/kotlin/com/yandex/yatagan/codegen/impl/AssistedInjectFactoryGenerator.kt +++ b/codegen/impl/src/main/kotlin/com/yandex/yatagan/codegen/impl/AssistedInjectFactoryGenerator.kt @@ -25,6 +25,7 @@ import com.yandex.yatagan.codegen.poetry.buildExpression import com.yandex.yatagan.core.graph.BindingGraph import com.yandex.yatagan.core.graph.bindings.AssistedInjectFactoryBinding import com.yandex.yatagan.core.model.AssistedInjectFactoryModel +import com.yandex.yatagan.core.model.ConditionScope import com.yandex.yatagan.core.model.component1 import com.yandex.yatagan.core.model.component2 import javax.inject.Inject @@ -38,13 +39,15 @@ internal class AssistedInjectFactoryGenerator @Inject constructor( ) : ComponentGenerator.Contributor { private val modelToImpl: Map by lazy { val classNamespace = Namespace() - thisGraph.localAssistedInjectFactories.associateWith { model -> - componentImplName.nestedClass(classNamespace.name( - nameModel = model.name, - suffix = "Impl", - firstCapital = true, - )) - } + thisGraph.localAssistedInjectFactories + .filter { it.value != ConditionScope.Never } + .mapValues { + componentImplName.nestedClass(classNamespace.name( + nameModel = it.key.name, + suffix = "Impl", + firstCapital = true, + )) + } } fun generateCreation( diff --git a/core/graph/api/api/api.api b/core/graph/api/api/api.api index 248e0d19..64c82bd0 100644 --- a/core/graph/api/api/api.api +++ b/core/graph/api/api/api.api @@ -3,7 +3,7 @@ public abstract interface class com/yandex/yatagan/core/graph/BindingGraph : com public abstract fun getCreator ()Lcom/yandex/yatagan/core/model/ComponentFactoryModel; public abstract fun getDependencies ()Ljava/util/Collection; public abstract fun getEntryPoints ()Ljava/util/Collection; - public abstract fun getLocalAssistedInjectFactories ()Ljava/util/Collection; + public abstract fun getLocalAssistedInjectFactories ()Ljava/util/Map; public abstract fun getLocalBindings ()Ljava/util/Map; public abstract fun getLocalConditionLiterals ()Ljava/util/Map; public abstract fun getMemberInjectors ()Ljava/util/Collection; diff --git a/core/graph/api/src/main/kotlin/com/yandex/yatagan/core/graph/BindingGraph.kt b/core/graph/api/src/main/kotlin/com/yandex/yatagan/core/graph/BindingGraph.kt index 556123dc..f573d2c7 100644 --- a/core/graph/api/src/main/kotlin/com/yandex/yatagan/core/graph/BindingGraph.kt +++ b/core/graph/api/src/main/kotlin/com/yandex/yatagan/core/graph/BindingGraph.kt @@ -71,7 +71,8 @@ public interface BindingGraph : MayBeInvalid, Extensible, /** * [AssistedInjectFactoryModel]s that are hosted in this graph. */ - public val localAssistedInjectFactories: Collection + @Incubating + public val localAssistedInjectFactories: Map /** * A collection of parent (not necessarily direct) [BindingGraph]s, from which bindings and/or conditions are used diff --git a/core/graph/impl/src/main/kotlin/com/yandex/yatagan/core/graph/impl/BindingGraphImpl.kt b/core/graph/impl/src/main/kotlin/com/yandex/yatagan/core/graph/impl/BindingGraphImpl.kt index a641ff48..10a36b4b 100644 --- a/core/graph/impl/src/main/kotlin/com/yandex/yatagan/core/graph/impl/BindingGraphImpl.kt +++ b/core/graph/impl/src/main/kotlin/com/yandex/yatagan/core/graph/impl/BindingGraphImpl.kt @@ -122,7 +122,7 @@ internal class BindingGraphImpl private constructor( internal val localNodes = mutableSetOf() override val localBindings = mutableMapOf() override val localConditionLiterals = mutableMapOf() - override val localAssistedInjectFactories = mutableSetOf() + override val localAssistedInjectFactories = mutableMapOf() override val usedParents = mutableSetOf() override val children: Collection @@ -210,7 +210,7 @@ internal class BindingGraphImpl private constructor( binding.accept(object : Binding.Visitor { override fun visitOther(binding: Binding) = Unit override fun visitAssistedInjectFactory(binding: AssistedInjectFactoryBinding) { - localAssistedInjectFactories += binding.model + localAssistedInjectFactories[binding.model] = binding.conditionScope } }) } @@ -259,7 +259,7 @@ internal class BindingGraphImpl private constructor( for (child in childrenSequence(includeThis = false)) { child as BindingGraphImpl val usesConditions = child.localConditionLiterals.keys.removeAll(localConditionLiterals.keys) - val usesAssistedInjectFactories = child.localAssistedInjectFactories.removeAll(localAssistedInjectFactories) + val usesAssistedInjectFactories = child.localAssistedInjectFactories.keys.removeAll(localAssistedInjectFactories.keys) if (usesConditions || usesAssistedInjectFactories) { // This will never be seen by materialization and that's okay, because no bindings are required here. child.usedParents += this diff --git a/testing/tests/src/test/kotlin/com/yandex/yatagan/testing/tests/ConditionsTest.kt b/testing/tests/src/test/kotlin/com/yandex/yatagan/testing/tests/ConditionsTest.kt index 922d9b42..767e0fb6 100644 --- a/testing/tests/src/test/kotlin/com/yandex/yatagan/testing/tests/ConditionsTest.kt +++ b/testing/tests/src/test/kotlin/com/yandex/yatagan/testing/tests/ConditionsTest.kt @@ -1216,4 +1216,52 @@ class ConditionsTest( compileRunAndValidate() } + + @Test + fun `issue #209 - assisted factory generated for unsupported variants`() { + includeFromSourceSet(flavors) + + givenKotlinSource("test.TestCase", """ + import com.yandex.yatagan.* + import javax.inject.* + + @AssistedFactory + interface PhoneAssistedFactory { + fun create(i: Int): PhoneAssistedClass + } + + @Conditional(onlyIn = [DeviceType.Phone::class]) + class PhoneAssistedClass @AssistedInject constructor( + phoneDependency: PhoneDependency, + @Assisted arg: Int, + ) + + @Conditional(onlyIn = [DeviceType.Phone::class]) + class PhoneDependency @Inject constructor() + + class CommonClass @Inject constructor( + phoneFactory: Optional, + ) + + interface CommonComponent { + fun getCommonClass(): CommonClass + } + + @Component(variant = [DeviceType.Phone::class]) + interface PhoneComponent: CommonComponent + + @Component(variant = [DeviceType.Tablet::class]) + interface TabletComponent: CommonComponent + + fun test() { + val phoneComponent = Yatagan.create(PhoneComponent::class.java) + val tabletComponent = Yatagan.create(TabletComponent::class.java) + + val phoneCommon = phoneComponent.getCommonClass() + val tabletCommon = tabletComponent.getCommonClass() + } + """.trimIndent()) + + compileRunAndValidate() + } } \ No newline at end of file diff --git a/testing/tests/src/test/resources/golden/ConditionsTest/issue-209-assisted-factory-generated-for-unsupported-variants.golden-code.txt b/testing/tests/src/test/resources/golden/ConditionsTest/issue-209-assisted-factory-generated-for-unsupported-variants.golden-code.txt new file mode 100644 index 00000000..03c53374 --- /dev/null +++ b/testing/tests/src/test/resources/golden/ConditionsTest/issue-209-assisted-factory-generated-for-unsupported-variants.golden-code.txt @@ -0,0 +1,101 @@ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Name: test/YataganTabletComponent.java +package test; + +import com.yandex.yatagan.AutoBuilder; +import com.yandex.yatagan.Optional; +import com.yandex.yatagan.internal.Checks; +import com.yandex.yatagan.internal.YataganGenerated; +import java.lang.Class; +import java.lang.Override; +import java.lang.SuppressWarnings; +import java.util.Collections; +import javax.annotation.processing.Generated; + +@SuppressWarnings({"unchecked", "rawtypes", "NullableProblems", "deprecation"}) +@YataganGenerated +@Generated("com.yandex.yatagan.codegen.impl.ComponentGenerator") +public final class YataganTabletComponent implements TabletComponent { + private YataganTabletComponent() { + } + + @Override + public CommonClass getCommonClass() { + return new CommonClass(Optional.empty()); + } + + public static AutoBuilder autoBuilder() { + return new AutoBuilderImpl(); + } + + private static final class AutoBuilderImpl implements AutoBuilder { + @Override + public final AutoBuilder provideInput(I input, + Class inputClass) { + Checks.reportUnexpectedAutoBuilderInput(input.getClass(), Collections.emptyList()); + return this; + } + + @Override + public final YataganTabletComponent create() { + return new YataganTabletComponent(); + } + } +} + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Name: test/YataganPhoneComponent.java +package test; + +import com.yandex.yatagan.AutoBuilder; +import com.yandex.yatagan.Optional; +import com.yandex.yatagan.internal.Checks; +import com.yandex.yatagan.internal.YataganGenerated; +import java.lang.Class; +import java.lang.Override; +import java.lang.SuppressWarnings; +import java.util.Collections; +import javax.annotation.processing.Generated; + +@SuppressWarnings({"unchecked", "rawtypes", "NullableProblems", "deprecation"}) +@YataganGenerated +@Generated("com.yandex.yatagan.codegen.impl.ComponentGenerator") +public final class YataganPhoneComponent implements PhoneComponent { + private YataganPhoneComponent() { + } + + @Override + public CommonClass getCommonClass() { + return new CommonClass(this.optOfPhoneAssistedFactory()); + } + + Optional optOfPhoneAssistedFactory() { + return Optional.of(this.new PhoneAssistedFactoryImpl()); + } + + public static AutoBuilder autoBuilder() { + return new AutoBuilderImpl(); + } + + private final class PhoneAssistedFactoryImpl implements PhoneAssistedFactory { + @Override + public PhoneAssistedClass create(int i) { + return new PhoneAssistedClass(new PhoneDependency(), i); + } + } + + private static final class AutoBuilderImpl implements AutoBuilder { + @Override + public final AutoBuilder provideInput(I input, Class inputClass) { + Checks.reportUnexpectedAutoBuilderInput(input.getClass(), Collections.emptyList()); + return this; + } + + @Override + public final YataganPhoneComponent create() { + return new YataganPhoneComponent(); + } + } +} + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~