diff --git a/dagger-compiler/main/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java b/dagger-compiler/main/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java index 6b9a5771137..fb2283e5cde 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java @@ -32,6 +32,7 @@ import static dagger.internal.codegen.xprocessing.XAnnotationSpecs.Suppression.UNCHECKED; import static dagger.internal.codegen.xprocessing.XAnnotationSpecs.suppressWarnings; import static dagger.internal.codegen.xprocessing.XCodeBlocks.makeParametersCodeBlock; +import static dagger.internal.codegen.xprocessing.XCodeBlocks.staticReferenceOf; import static dagger.internal.codegen.xprocessing.XElements.asMethod; import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; import static dagger.internal.codegen.xprocessing.XFunSpecs.constructorBuilder; @@ -367,15 +368,16 @@ private XFunSpec callProducesMethod(ProductionBinding binding, FactoryFields fac XCodeBlock.of( "%L.%N(%L)", factoryFields.moduleField.isPresent() - ? factoryFields.moduleField.get().getName() // SUPPRESS_GET_NAME_CHECK - : XCodeBlock.of("%T", binding.bindingTypeElement().get().asClassName()), + ? XCodeBlock.of("%N", factoryFields.moduleField.get()) + : staticReferenceOf(binding.bindingTypeElement().get()), getSimpleName(binding.bindingElement().get()), makeParametersCodeBlock(parameterCodeBlocks.build())); XCodeBlock returnCodeBlock; switch (ProductionKind.fromProducesMethod(asMethod(binding.bindingElement().get()))) { case IMMEDIATE: - returnCodeBlock = XCodeBlock.of("%T.immediateFuture(%L)", XTypeNames.FUTURES, moduleCodeBlock); + returnCodeBlock = + XCodeBlock.of("%T.immediateFuture(%L)", XTypeNames.FUTURES, moduleCodeBlock); break; case FUTURE: returnCodeBlock = XCodeBlock.of("%L", moduleCodeBlock); diff --git a/javatests/dagger/functional/producers/kotlin/BUILD b/javatests/dagger/functional/producers/kotlin/BUILD new file mode 100644 index 00000000000..59713a3f0a0 --- /dev/null +++ b/javatests/dagger/functional/producers/kotlin/BUILD @@ -0,0 +1,44 @@ +# Copyright (C) 2024 The Dagger Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Description: +# Producers test code with Kotlin language features + +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +load("//:test_defs.bzl", "GenJavaTests") + +package(default_visibility = ["//:src"]) + +GenJavaTests( + name = "ObjectModuleTest", + srcs = ["ObjectModuleTest.java"], + deps = [ + ":ObjectModuleClasses", + "//third_party/java/guava/util/concurrent", + "//third_party/java/junit", + "//third_party/java/truth", + ], +) + +kt_jvm_library( + name = "ObjectModuleClasses", + srcs = [ + "ObjectModuleClasses.kt", + ], + deps = [ + "//third_party/java/dagger", + "//third_party/java/dagger:producers", + "//third_party/java/guava/util/concurrent", + ], +) diff --git a/javatests/dagger/functional/producers/kotlin/ObjectModuleClasses.kt b/javatests/dagger/functional/producers/kotlin/ObjectModuleClasses.kt new file mode 100644 index 00000000000..8f699de822a --- /dev/null +++ b/javatests/dagger/functional/producers/kotlin/ObjectModuleClasses.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2024 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.functional.producers.kotlin + +import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.MoreExecutors +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoSet +import dagger.producers.ProducerModule +import dagger.producers.Produces +import dagger.producers.Production +import dagger.producers.ProductionComponent +import java.util.concurrent.Executor +import javax.inject.Named + +const val OBJECT_MODULE_VALUE = "object_module" +const val OBJECT_MODULE_STATIC_VALUE = "object_module_static" +const val OBJECT_MODULE_SET_VALUE = "object_module_set" +const val NESTED_OBJECT_MODULE_VALUE = "nested_object_module" +const val INTERFACE_COMPANION_OBJECT_MODULE_VALUE = "interface_companion_object_module" +const val NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE = + "nested_interface_companion_object_module" +const val CLASS_COMPANION_OBJECT_MODULE_VALUE = "class_companion_object_module" + +// Regression test for b/347108703 +@ProductionComponent( + modules = + [ + ExecutorModule::class, + TestKotlinObjectModule::class, + TestModuleForNesting.TestNestedKotlinObjectModule::class, + TestInterfaceCompanionObjectModule::class, + TestModuleForNesting.TestNestedInterfaceCompanionObjectModule::class, + TestClassCompanionObjectModule::class, + ] +) +interface TestKotlinComponentWithObjectModule { + @Named(OBJECT_MODULE_VALUE) fun getObjectModuleData(): ListenableFuture + + @Named(OBJECT_MODULE_STATIC_VALUE) fun getObjectModuleStaticData(): ListenableFuture + + @Named(NESTED_OBJECT_MODULE_VALUE) fun getNestedObjectModuleData(): ListenableFuture + + @Named(INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + fun getInterfaceCompanionObjectModuleData(): ListenableFuture + + @Named(NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + fun getNestedInterfaceCompanionObjectModuleData(): ListenableFuture + + @Named(CLASS_COMPANION_OBJECT_MODULE_VALUE) + fun getClassCompanionObjectModuleData(): ListenableFuture + + @Named(OBJECT_MODULE_SET_VALUE) fun getSetOfData(): ListenableFuture> +} + +@ProducerModule +object TestKotlinObjectModule { + @Produces @Named(OBJECT_MODULE_VALUE) fun provideData() = TestData(OBJECT_MODULE_VALUE) + + @Produces + @JvmStatic + @Named(OBJECT_MODULE_STATIC_VALUE) + fun provideStaticData() = TestData(OBJECT_MODULE_STATIC_VALUE) + + @Produces + @IntoSet + @Named(OBJECT_MODULE_SET_VALUE) + fun provideSetData() = TestData(OBJECT_MODULE_SET_VALUE) +} + +class TestModuleForNesting { + @ProducerModule + object TestNestedKotlinObjectModule { + @Produces + @Named(NESTED_OBJECT_MODULE_VALUE) + fun provideData() = TestData(NESTED_OBJECT_MODULE_VALUE) + } + + @ProducerModule + interface TestNestedInterfaceCompanionObjectModule { + companion object { + @Produces + @Named(NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + fun provideData() = TestData(NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + } + } +} + +@ProducerModule +interface TestInterfaceCompanionObjectModule { + companion object { + @Produces + @Named(INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + fun provideData() = TestData(INTERFACE_COMPANION_OBJECT_MODULE_VALUE) + } +} + +@Suppress("ClassShouldBeObject") +@ProducerModule +class TestClassCompanionObjectModule { + companion object { + @Produces + @Named(CLASS_COMPANION_OBJECT_MODULE_VALUE) + fun provideData() = TestData(CLASS_COMPANION_OBJECT_MODULE_VALUE) + } +} + +data class TestData(val data: String) + +@Module +object ExecutorModule { + @Provides @Production fun executor(): Executor = MoreExecutors.directExecutor() +} diff --git a/javatests/dagger/functional/producers/kotlin/ObjectModuleTest.java b/javatests/dagger/functional/producers/kotlin/ObjectModuleTest.java new file mode 100644 index 00000000000..27b31ea5be7 --- /dev/null +++ b/javatests/dagger/functional/producers/kotlin/ObjectModuleTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.functional.producers.kotlin; + +import static com.google.common.truth.Truth.assertThat; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.CLASS_COMPANION_OBJECT_MODULE_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.INTERFACE_COMPANION_OBJECT_MODULE_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.NESTED_OBJECT_MODULE_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.OBJECT_MODULE_SET_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.OBJECT_MODULE_STATIC_VALUE; +import static dagger.functional.producers.kotlin.ObjectModuleClassesKt.OBJECT_MODULE_VALUE; + +import com.google.common.util.concurrent.ListenableFuture; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ObjectModuleTest { + + @Test + public void verifyObjectModule() throws Exception { + TestKotlinComponentWithObjectModule component = + DaggerTestKotlinComponentWithObjectModule.create(); + assertThat(component.getObjectModuleData().get().getData()).isEqualTo(OBJECT_MODULE_VALUE); + assertThat(component.getObjectModuleStaticData().get().getData()) + .isEqualTo(OBJECT_MODULE_STATIC_VALUE); + assertThat(component.getNestedObjectModuleData().get().getData()) + .isEqualTo(NESTED_OBJECT_MODULE_VALUE); + assertThat(component.getInterfaceCompanionObjectModuleData().get().getData()) + .isEqualTo(INTERFACE_COMPANION_OBJECT_MODULE_VALUE); + assertThat(component.getNestedInterfaceCompanionObjectModuleData().get().getData()) + .isEqualTo(NESTED_INTERFACE_COMPANION_OBJECT_MODULE_VALUE); + assertThat(component.getClassCompanionObjectModuleData().get().getData()) + .isEqualTo(CLASS_COMPANION_OBJECT_MODULE_VALUE); + ListenableFuture> setFuture = component.getSetOfData(); + assertThat(setFuture).isNotNull(); + assertThat(setFuture.get()).hasSize(1); + assertThat(setFuture.get().iterator().next().getData()).isEqualTo(OBJECT_MODULE_SET_VALUE); + } +}