Disallow providing or injecting `dagger.internal.Provider`. This will help with IDEs accidentally suggesting importing `dagger.internal.Provider`.
Also disallow injections of raw Provider in Maps, for both javax and dagger Providers. This matches the policy of disallowing raw Provider injections in general.
RELNOTES=Disallow providing or injecting `dagger.internal.Provider`. Also disallow injections of raw Provider in Maps, for both javax and dagger Providers.
PiperOrigin-RevId: 713755026
diff --git a/java/dagger/internal/codegen/base/FrameworkTypes.java b/java/dagger/internal/codegen/base/FrameworkTypes.java
index e607f76..04eaf2e 100644
--- a/java/dagger/internal/codegen/base/FrameworkTypes.java
+++ b/java/dagger/internal/codegen/base/FrameworkTypes.java
@@ -55,6 +55,12 @@
TypeNames.PROVIDER,
TypeNames.JAKARTA_PROVIDER);
+ // This is a set of types that are disallowed from use, but also aren't framework types in the
+ // sense that they aren't supported. Like we shouldn't try to unwrap these if we see them, though
+ // we shouldn't see them at all if they are correctly caught in validation.
+ private static final ImmutableSet<ClassName> DISALLOWED_TYPES =
+ ImmutableSet.of(TypeNames.DAGGER_PROVIDER);
+
/** Returns true if the type represents a producer-related framework type. */
public static boolean isProducerType(XType type) {
return typeIsOneOf(PRODUCTION_TYPES, type);
@@ -73,6 +79,10 @@
return typeIsOneOf(MAP_VALUE_FRAMEWORK_TYPES, type);
}
+ public static boolean isDisallowedType(XType type) {
+ return typeIsOneOf(DISALLOWED_TYPES, type);
+ }
+
private static boolean typeIsOneOf(Set<ClassName> classNames, XType type) {
return classNames.stream().anyMatch(className -> isTypeOf(type, className));
}
diff --git a/java/dagger/internal/codegen/validation/BindingElementValidator.java b/java/dagger/internal/codegen/validation/BindingElementValidator.java
index 63ecb64..93f4f07 100644
--- a/java/dagger/internal/codegen/validation/BindingElementValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingElementValidator.java
@@ -42,6 +42,7 @@
import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.model.Scope;
import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
@@ -176,6 +177,9 @@
protected void checkType() {
switch (ContributionType.fromBindingElement(element)) {
case UNIQUE:
+ // Basic checks on the types
+ bindingElementType().ifPresent(this::checkKeyType);
+
// Validate that a unique binding is not attempting to bind a framework type. This
// validation is only appropriate for unique bindings because multibindings may collect
// framework types. E.g. Set<Provider<Foo>> is perfectly reasonable.
@@ -185,17 +189,22 @@
// This validation is only appropriate for unique bindings because multibindings may
// collect assisted types.
checkAssistedType();
- // fall through
+
+ // Check for any specifically disallowed types
+ bindingElementType().ifPresent(this::checkDisallowedType);
+ break;
case SET:
bindingElementType().ifPresent(this::checkSetValueFrameworkType);
break;
+
case MAP:
bindingElementType().ifPresent(this::checkMapValueFrameworkType);
break;
case SET_VALUES:
checkSetValuesType();
+ break;
}
}
@@ -360,7 +369,8 @@
*/
private void checkFrameworkType() {
if (bindingElementType().filter(FrameworkTypes::isFrameworkType).isPresent()) {
- report.addError(bindingElements("must not %s framework types", bindingElementTypeVerb()));
+ report.addError(bindingElements("must not %s framework types: %s",
+ bindingElementTypeVerb(), XTypes.toStableString(bindingElementType().get())));
}
}
@@ -368,16 +378,28 @@
checkKeyType(bindingType);
if (FrameworkTypes.isSetValueFrameworkType(bindingType)) {
report.addError(bindingElements(
- "with @IntoSet/@ElementsIntoSet must not %s framework types",
- bindingElementTypeVerb()));
+ "with @IntoSet/@ElementsIntoSet must not %s framework types: %s",
+ bindingElementTypeVerb(), XTypes.toStableString(bindingType)));
}
+ checkDisallowedType(bindingType);
}
private void checkMapValueFrameworkType(XType bindingType) {
checkKeyType(bindingType);
if (FrameworkTypes.isMapValueFrameworkType(bindingType)) {
report.addError(
- bindingElements("with @IntoMap must not %s framework types", bindingElementTypeVerb()));
+ bindingElements("with @IntoMap must not %s framework types: %s",
+ bindingElementTypeVerb(), XTypes.toStableString(bindingType)));
+ }
+ checkDisallowedType(bindingType);
+ }
+
+ private void checkDisallowedType(XType bindingType) {
+ // TODO(erichang): Consider if we want to go inside complex types to ban
+ // dagger.internal.Provider as well? E.g. List<dagger.internal.Provider<Foo>>
+ if (FrameworkTypes.isDisallowedType(bindingType)) {
+ report.addError(bindingElements("must not %s disallowed types: %s",
+ bindingElementTypeVerb(), XTypes.toStableString(bindingElementType().get())));
}
}
}
diff --git a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
index 5510048..87bcad0 100644
--- a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
+++ b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
@@ -18,7 +18,9 @@
import static androidx.room.compiler.processing.XElementKt.isField;
import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static dagger.internal.codegen.base.FrameworkTypes.isDisallowedType;
import static dagger.internal.codegen.base.FrameworkTypes.isFrameworkType;
+import static dagger.internal.codegen.base.FrameworkTypes.isMapValueFrameworkType;
import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedInjectionType;
@@ -40,6 +42,7 @@
import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableSet;
import dagger.internal.codegen.base.FrameworkTypes;
+import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.base.RequestKinds;
import dagger.internal.codegen.binding.InjectionAnnotations;
import dagger.internal.codegen.javapoet.TypeNames;
@@ -154,6 +157,14 @@
// will just be noise.
return;
}
+ if (isDisallowedType(requestType)) {
+ report.addError(
+ "Dagger disallows injecting the type: " + XTypes.toStableString(requestType),
+ requestElement);
+ // If the requested type is a disallowed type then skip the remaining checks as they
+ // will just be noise.
+ return;
+ }
XType keyType = extractKeyType(requestType);
if (qualifiers.isEmpty() && isDeclared(keyType)) {
XTypeElement typeElement = keyType.getTypeElement();
@@ -191,6 +202,24 @@
requestElement, keyType.getTypeArguments().get(0)));
}
}
+ if (MapType.isMap(keyType)) {
+ MapType mapType = MapType.from(keyType);
+ if (!mapType.isRawType()) {
+ XType valueType = mapType.valueType();
+ if (isMapValueFrameworkType(valueType) && isRawParameterizedType(valueType)) {
+ report.addError(
+ "Dagger does not support injecting maps of raw framework types: "
+ + XTypes.toStableString(requestType),
+ requestElement);
+ }
+ if (isDisallowedType(valueType)) {
+ report.addError(
+ "Dagger does not support injecting maps of disallowed types: "
+ + XTypes.toStableString(requestType),
+ requestElement);
+ }
+ }
+ }
}
}
diff --git a/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
index 18cc734..a3a7ffa 100644
--- a/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
@@ -187,4 +187,28 @@
});
}
+ @Test
+ public void bindsInstanceDaggerProvider() {
+ Source bindsDaggerProvider =
+ CompilerTests.javaSource(
+ "test.BindsInstanceFrameworkType",
+ "package test;",
+ "",
+ "import dagger.BindsInstance;",
+ "import dagger.internal.Provider;",
+ "import dagger.producers.Producer;",
+ "",
+ "interface BindsInstanceFrameworkType {",
+ " @BindsInstance void bindsProvider(Provider<Object> objectProvider);",
+ "}");
+ CompilerTests.daggerCompiler(bindsDaggerProvider)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining("@BindsInstance parameters must not be disallowed types")
+ .onSource(bindsDaggerProvider)
+ .onLine(8);
+ });
+ }
}
diff --git a/javatests/dagger/internal/codegen/MembersInjectionTest.java b/javatests/dagger/internal/codegen/MembersInjectionTest.java
index b547ec0..ee02f9f 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionTest.java
@@ -779,33 +779,6 @@
}
@Test
- public void rawFrameworkTypeField() {
- Source file =
- CompilerTests.javaSource(
- "test.RawFrameworkTypes",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "class RawProviderField {",
- " @Inject",
- " Provider fieldWithRawProvider;",
- "}");
-
- CompilerTests.daggerCompiler(file)
- .withProcessingOptions(compilerMode.processorOptions())
- .compile(
- subject -> {
- subject.hasErrorCount(1);
- subject.hasErrorContaining(
- "Dagger does not support injecting raw type: javax.inject.Provider")
- .onSource(file)
- .onLineContaining("Provider fieldWithRawProvider");
- });
- }
-
- @Test
public void throwExceptionInjectedMethod() {
Source file =
CompilerTests.javaSource(
@@ -831,10 +804,37 @@
}
@Test
+ public void rawFrameworkTypeField() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.RawProviderField",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class RawProviderField {",
+ " @Inject",
+ " Provider fieldWithRawProvider;",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting raw type: javax.inject.Provider")
+ .onSource(file)
+ .onLineContaining("Provider fieldWithRawProvider");
+ });
+ }
+
+ @Test
public void rawFrameworkMethodTypeParameter() {
Source file =
CompilerTests.javaSource(
- "test.RawFrameworkTypes",
+ "test.RawProviderParameter",
"package test;",
"",
"import javax.inject.Inject;",
@@ -862,7 +862,7 @@
public void rawFrameworkConstructorTypeParameter() {
Source file =
CompilerTests.javaSource(
- "test.RawFrameworkTypes",
+ "test.RawProviderParameter",
"package test;",
"",
"import dagger.Component;",
@@ -888,6 +888,274 @@
}
@Test
+ public void rawMapFrameworkConstructorTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.RawMapProviderParameter",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "import java.util.Map;",
+ "",
+ "class RawMapProviderParameter {",
+ " @Inject",
+ " RawMapProviderParameter(",
+ " Map<String, Provider> rawProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting maps of raw framework types: "
+ + "java.util.Map<java.lang.String,javax.inject.Provider>")
+ .onSource(file)
+ .onLineContaining("Map<String, Provider> rawProviderParameter");
+ });
+ }
+
+ @Test
+ public void daggerProviderField() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerProviderField",
+ "package test;",
+ "",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "",
+ "class DaggerProviderField {",
+ " @Inject",
+ " Provider<String> fieldWithDaggerProvider;",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger disallows injecting the type: "
+ + "dagger.internal.Provider<java.lang.String>")
+ .onSource(file)
+ .onLineContaining("Provider<String> fieldWithDaggerProvider");
+ });
+ }
+
+ @Test
+ public void daggerProviderMethodTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerProviderParameter",
+ "package test;",
+ "",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "",
+ "class DaggerProviderParameter {",
+ " @Inject",
+ " void methodInjection(",
+ " Provider<String> daggerProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger disallows injecting the type: "
+ + "dagger.internal.Provider<java.lang.String>")
+ .onSource(file)
+ .onLineContaining("Provider<String> daggerProviderParameter");
+ });
+ }
+
+ @Test
+ public void daggerProviderConstructorTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerProviderParameter",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "",
+ "class DaggerProviderParameter {",
+ " @Inject",
+ " DaggerProviderParameter(",
+ " Provider<String> daggerProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger disallows injecting the type: "
+ + "dagger.internal.Provider<java.lang.String>")
+ .onSource(file)
+ .onLineContaining("Provider<String> daggerProviderParameter");
+ });
+ }
+
+ @Test
+ public void rawDaggerProviderConstructorTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.RawDaggerProviderParameter",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "",
+ "class RawDaggerProviderParameter {",
+ " @Inject",
+ " RawDaggerProviderParameter(",
+ " Provider rawDaggerProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger disallows injecting the type: dagger.internal.Provider")
+ .onSource(file)
+ .onLineContaining("Provider rawDaggerProviderParameter");
+ });
+ }
+
+ @Test
+ public void daggerMapProviderField() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerMapProviderField",
+ "package test;",
+ "",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "import java.util.Map;",
+ "",
+ "class DaggerMapProviderField {",
+ " @Inject",
+ " Map<String, Provider<Long>> fieldWithDaggerMapProvider;",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting maps of disallowed types: "
+ + "java.util.Map<java.lang.String,dagger.internal.Provider<java.lang.Long>>")
+ .onSource(file)
+ .onLineContaining("Map<String, Provider<Long>> fieldWithDaggerMapProvider");
+ });
+ }
+
+ @Test
+ public void daggerMapProviderMethodTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerMapProviderParameter",
+ "package test;",
+ "",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "import java.util.Map;",
+ "",
+ "class DaggerMapProviderParameter {",
+ " @Inject",
+ " void methodInjection(",
+ " Map<String, Provider<Long>> daggerMapProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting maps of disallowed types: "
+ + "java.util.Map<java.lang.String,dagger.internal.Provider<java.lang.Long>>")
+ .onSource(file)
+ .onLineContaining("Map<String, Provider<Long>> daggerMapProviderParameter");
+ });
+ }
+
+ @Test
+ public void daggerMapProviderConstructorTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.DaggerMapProviderParameter",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "import java.util.Map;",
+ "",
+ "class DaggerMapProviderParameter {",
+ " @Inject",
+ " DaggerMapProviderParameter(",
+ " Map<String, Provider<Long>> daggerMapProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting maps of disallowed types: "
+ + "java.util.Map<java.lang.String,dagger.internal.Provider<java.lang.Long>>")
+ .onSource(file)
+ .onLineContaining("Map<String, Provider<Long>> daggerMapProviderParameter");
+ });
+ }
+
+ @Test
+ public void rawDaggerMapProviderConstructorTypeParameter() {
+ Source file =
+ CompilerTests.javaSource(
+ "test.RawDaggerMapProviderParameter",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.Provider;",
+ "import javax.inject.Inject;",
+ "import java.util.Map;",
+ "",
+ "class RawDaggerMapProviderParameter {",
+ " @Inject",
+ " RawDaggerMapProviderParameter(",
+ " Map<String, Provider> rawDaggerMapProviderParameter) {}",
+ "}");
+
+ CompilerTests.daggerCompiler(file)
+ .withProcessingOptions(compilerMode.processorOptions())
+ .compile(
+ subject -> {
+ subject.hasErrorCount(1);
+ subject.hasErrorContaining(
+ "Dagger does not support injecting maps of disallowed types: "
+ + "java.util.Map<java.lang.String,dagger.internal.Provider>")
+ .onSource(file)
+ .onLineContaining("Map<String, Provider> rawDaggerMapProviderParameter");
+ });
+ }
+
+ @Test
public void injectsPrimitive() throws Exception {
Source injectedType =
CompilerTests.javaSource(
diff --git a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
index d315b77..ccff741 100644
--- a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
@@ -78,6 +78,19 @@
}
@Test
+ public void providesMethodReturnsDaggerInternalProvider() {
+ assertThatModuleMethod("@Provides dagger.internal.Provider<String> provideProvider() {}")
+ .hasError("@Provides methods must not return disallowed types");
+ }
+
+ @Test
+ public void providesIntoSetMethodReturnsDaggerInternalProvider() {
+ assertThatModuleMethod(
+ "@Provides @IntoSet dagger.internal.Provider<String> provideProvider() {}")
+ .hasError("@Provides methods must not return disallowed types");
+ }
+
+ @Test
public void providesMethodReturnsLazy() {
assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}")
.hasError("@Provides methods must not return framework types");
@@ -118,12 +131,31 @@
.hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set");
}
+ @Test public void providesElementsIntoSetMethodReturnsSetDaggerProvider() {
+ assertThatModuleMethod(
+ "@Provides @ElementsIntoSet Set<dagger.internal.Provider<String>> provideProvider() {}")
+ .hasError("@Provides methods must not return disallowed types");
+ }
+
@Test public void providesMethodSetValuesNotASet() {
assertThatModuleMethod(
"@Provides @ElementsIntoSet List<String> provideStrings() { return null; }")
.hasError("@Provides methods annotated with @ElementsIntoSet must return a Set");
}
+ @Test
+ public void bindsMethodReturnsProvider() {
+ assertThatModuleMethod("@Binds abstract Provider<Number> bindsProvider(Provider<Long> impl);")
+ .hasError("@Binds methods must not return framework types");
+ }
+
+ @Test
+ public void bindsMethodReturnsDaggerProvider() {
+ assertThatModuleMethod("@Binds abstract dagger.internal.Provider<Number> "
+ + "bindsProvider(dagger.internal.Provider<Long> impl);")
+ .hasError("@Binds methods must not return disallowed types");
+ }
+
@Test public void modulesWithTypeParamsMustBeAbstract() {
Source moduleFile =
CompilerTests.javaSource(