From 56e1eebae08889708c0b7521e6171625d4f54ad2 Mon Sep 17 00:00:00 2001 From: Brad Corso Date: Tue, 16 Jul 2024 15:35:20 -0700 Subject: [PATCH] Internal changes RELNOTES=N/A PiperOrigin-RevId: 653003160 --- .../codegen/binding/BindingGraphFactory.java | 23 +- .../internal/codegen/MultibindingTest.java | 227 ++++++++++++++++++ 2 files changed, 239 insertions(+), 11 deletions(-) diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java index 79a93d5b7b9..5a21479f22f 100644 --- a/java/dagger/internal/codegen/binding/BindingGraphFactory.java +++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java @@ -278,18 +278,19 @@ ResolvedBindings lookUpBindings(Key requestKey) { Set subcomponentDeclarations = new LinkedHashSet<>(); // Gather all bindings, multibindings, optional, and subcomponent declarations/contributions. - ImmutableSet keysMatchingRequest = keysMatchingRequest(requestKey); + ImmutableSet multibindingKeysMatchingRequest = + multibindingKeysMatchingRequest(requestKey); for (Resolver resolver : getResolverLineage()) { bindings.addAll(resolver.getLocalExplicitBindings(requestKey)); + subcomponentDeclarations.addAll(resolver.declarations.subcomponents(requestKey)); + // The optional binding declarations are keyed by the unwrapped type. + keyFactory.unwrapOptional(requestKey) + .map(resolver.declarations::optionalBindings) + .ifPresent(optionalBindingDeclarations::addAll); - for (Key key : keysMatchingRequest) { + for (Key key : multibindingKeysMatchingRequest) { multibindingContributions.addAll(resolver.getLocalMultibindingContributions(key)); multibindingDeclarations.addAll(resolver.declarations.multibindings(key)); - subcomponentDeclarations.addAll(resolver.declarations.subcomponents(key)); - // The optional binding declarations are keyed by the unwrapped type. - keyFactory.unwrapOptional(key) - .map(resolver.declarations::optionalBindings) - .ifPresent(optionalBindingDeclarations::addAll); } } @@ -425,12 +426,12 @@ private void addSubcomponentToOwningResolver(ContributionBinding subcomponentCre * javac users) * */ - private ImmutableSet keysMatchingRequest(Key requestKey) { + private ImmutableSet multibindingKeysMatchingRequest(Key requestKey) { return keysMatchingRequestCache.computeIfAbsent( - requestKey, this::keysMatchingRequestUncached); + requestKey, this::multibindingKeysMatchingRequestUncached); } - private ImmutableSet keysMatchingRequestUncached(Key requestKey) { + private ImmutableSet multibindingKeysMatchingRequestUncached(Key requestKey) { ImmutableSet.Builder keys = ImmutableSet.builder(); keys.add(requestKey); keyFactory.unwrapSetKey(requestKey, TypeNames.PRODUCED).ifPresent(keys::add); @@ -867,7 +868,7 @@ private boolean hasLocalBindings(ResolvedBindings resolvedBindings) { * this component's modules that matches the key. */ private boolean hasLocalMultibindingContributions(Key requestKey) { - return keysMatchingRequest(requestKey) + return multibindingKeysMatchingRequest(requestKey) .stream() .anyMatch(key -> !getLocalMultibindingContributions(key).isEmpty()); } diff --git a/javatests/dagger/internal/codegen/MultibindingTest.java b/javatests/dagger/internal/codegen/MultibindingTest.java index 1fee6f2c68b..60cbfeff896 100644 --- a/javatests/dagger/internal/codegen/MultibindingTest.java +++ b/javatests/dagger/internal/codegen/MultibindingTest.java @@ -16,6 +16,7 @@ package dagger.internal.codegen; +import androidx.room.compiler.processing.XProcessingEnv; import androidx.room.compiler.processing.util.Source; import dagger.testing.compile.CompilerTests; import org.junit.Test; @@ -299,4 +300,230 @@ public void provideExplicitSetInParent_AndMultibindingContributionInChild() { .onLineContaining("interface Parent"); }); } + + // Regression test for b/352142595. + @Test + public void testMultibindingMapWithKotlinSource() { + Source parent = + CompilerTests.kotlinSource( + "test.Parent.kt", + "package test", + "", + "import dagger.Component", + "", + "@Component(modules = [ParentModule::class])", + "interface Parent {", + " fun usage(): Usage", + "}"); + Source usage = + CompilerTests.kotlinSource( + "test.Usage.kt", + "package test", + "", + "import javax.inject.Inject", + "", + "class Usage @Inject constructor(map: Map)"); + Source parentModule = + CompilerTests.kotlinSource( + "test.ParentModule.kt", + "@file:Suppress(\"INLINE_FROM_HIGHER_PLATFORM\")", // Required to use TODO() + "package test", + "", + "import dagger.Module", + "import dagger.Provides", + "import dagger.multibindings.IntoMap", + "import dagger.multibindings.StringKey", + "", + "@Module", + "class ParentModule {", + " @Provides", + " @IntoMap", + " @StringKey(\"key\")", + " fun provideMyInterface(): MyInterface = TODO()", + "}"); + Source myInterface = + CompilerTests.kotlinSource( + "test.MyInterface.kt", + "package test", + "", + "interface MyInterface"); + + CompilerTests.daggerCompiler(parent, parentModule, myInterface, usage) + .compile( + subject -> { + // TODO(b/284207175): Due to a bug in our KSP implementation, KSP and Javac behave + // differently. Rather than cause a breaking change by fixing this bug directly, we've + // decided to wait until b/284207175 is implemented. + if (CompilerTests.backend(subject) == XProcessingEnv.Backend.KSP) { + subject.hasErrorCount(0); + } else { + subject.hasErrorCount(1); + subject.hasErrorContaining("Map cannot be provided"); + } + }); + } + + // Regression test for b/352142595. + @Test + public void testMultibindingMapProviderWithKotlinSource() { + Source parent = + CompilerTests.kotlinSource( + "test.Parent.kt", + "package test", + "", + "import dagger.Component", + "", + "@Component(modules = [ParentModule::class])", + "interface Parent {", + " fun usage(): Usage", + "}"); + Source usage = + CompilerTests.kotlinSource( + "test.Usage.kt", + "package test", + "", + "import javax.inject.Inject", + "import javax.inject.Provider", + "", + "class Usage @Inject constructor(map: Map>)"); + Source parentModule = + CompilerTests.kotlinSource( + "test.ParentModule.kt", + "@file:Suppress(\"INLINE_FROM_HIGHER_PLATFORM\")", // Required to use TODO() + "package test", + "", + "import dagger.Module", + "import dagger.Provides", + "import dagger.multibindings.IntoMap", + "import dagger.multibindings.StringKey", + "", + "@Module", + "class ParentModule {", + " @Provides", + " @IntoMap", + " @StringKey(\"key\")", + " fun provideMyInterface(): MyInterface = TODO()", + "}"); + Source myInterface = + CompilerTests.kotlinSource( + "test.MyInterface.kt", + "package test", + "", + "interface MyInterface"); + + CompilerTests.daggerCompiler(parent, parentModule, myInterface, usage) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining( + "Map> cannot be provided"); + }); + } + + // Regression test for b/352142595. + @Test + public void testMultibindingSetWithKotlinSource() { + Source parent = + CompilerTests.kotlinSource( + "test.Parent.kt", + "package test", + "", + "import dagger.Component", + "", + "@Component(modules = [ParentModule::class])", + "interface Parent {", + " fun usage(): Usage", + "}"); + Source usage = + CompilerTests.kotlinSource( + "test.Usage.kt", + "package test", + "", + "import javax.inject.Inject", + "", + "class Usage @Inject constructor(set: Set)"); + Source parentModule = + CompilerTests.kotlinSource( + "test.ParentModule.kt", + "@file:Suppress(\"INLINE_FROM_HIGHER_PLATFORM\")", // Required to use TODO() + "package test", + "", + "import dagger.Module", + "import dagger.Provides", + "import dagger.multibindings.IntoSet", + "", + "@Module", + "class ParentModule {", + " @Provides", + " @IntoSet", + " fun provideMyInterface(): MyInterface = TODO()", + "}"); + Source myInterface = + CompilerTests.kotlinSource( + "test.MyInterface.kt", + "package test", + "", + "interface MyInterface"); + + CompilerTests.daggerCompiler(parent, parentModule, myInterface, usage) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining("Set cannot be provided"); + }); + } + + // Regression test for b/352142595. + @Test + public void testMultibindingSetProviderWithKotlinSource() { + Source parent = + CompilerTests.kotlinSource( + "test.Parent.kt", + "package test", + "", + "import dagger.Component", + "", + "@Component(modules = [ParentModule::class])", + "interface Parent {", + " fun usage(): Usage", + "}"); + Source usage = + CompilerTests.kotlinSource( + "test.Usage.kt", + "package test", + "", + "import javax.inject.Inject", + "import javax.inject.Provider", + "", + "class Usage @Inject constructor(set: Set>)"); + Source parentModule = + CompilerTests.kotlinSource( + "test.ParentModule.kt", + "@file:Suppress(\"INLINE_FROM_HIGHER_PLATFORM\")", // Required to use TODO() + "package test", + "", + "import dagger.Module", + "import dagger.Provides", + "import dagger.multibindings.IntoSet", + "", + "@Module", + "class ParentModule {", + " @Provides", + " @IntoSet", + " fun provideMyInterface(): MyInterface = TODO()", + "}"); + Source myInterface = + CompilerTests.kotlinSource( + "test.MyInterface.kt", + "package test", + "", + "interface MyInterface"); + + CompilerTests.daggerCompiler(parent, parentModule, myInterface, usage) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining("Set> cannot be provided"); + }); + } }