aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorronshapiro <ronshapiro@google.com>2017-10-09 13:24:09 -0700
committerRon Shapiro <shapiro.rd@gmail.com>2017-10-09 17:59:15 -0400
commite8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8 (patch)
treec326d6623dc471564e723e1f987e80189014d0b4
parent6d1e6acc5fd8ac20d2cf83d20e3a9ed946f75943 (diff)
downloaddagger2-e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8.tar.gz
Inline requests for @Binds bindingsupstream/dagger-2.12
This is a rollforward of e2bff35e and fixes an issue where Expressions for inline Foo_Factory.create() were not properly typed internally. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171578370
-rw-r--r--java/dagger/internal/codegen/BindingExpression.java4
-rw-r--r--java/dagger/internal/codegen/BindsMethodValidator.java86
-rw-r--r--java/dagger/internal/codegen/BindsTypeChecker.java105
-rw-r--r--java/dagger/internal/codegen/DelegateBindingExpression.java137
-rw-r--r--java/dagger/internal/codegen/DependencyRequest.java17
-rw-r--r--java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java16
-rw-r--r--javatests/dagger/functional/binds/AccessesExposedComponent.java44
-rw-r--r--javatests/dagger/functional/binds/subpackage/Exposed.java19
-rw-r--r--javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java19
-rw-r--r--javatests/dagger/functional/binds/subpackage/ExposedModule.java43
-rw-r--r--javatests/dagger/functional/binds/subpackage/NotExposed.java26
-rw-r--r--javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java25
-rw-r--r--javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java25
-rw-r--r--javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java1012
-rw-r--r--javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java10
15 files changed, 1502 insertions, 86 deletions
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index caf649b0a..58506f405 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -217,6 +217,10 @@ abstract class BindingExpression {
return new OptionalBindingExpression(
provisionBinding, bindingExpression, componentBindingExpressions, types);
+ case SYNTHETIC_DELEGATE_BINDING:
+ return DelegateBindingExpression.create(
+ graph, bindingExpression, componentBindingExpressions, types, elements);
+
case BUILDER_BINDING:
return new BoundInstanceBindingExpression(
bindingExpression,
diff --git a/java/dagger/internal/codegen/BindsMethodValidator.java b/java/dagger/internal/codegen/BindsMethodValidator.java
index 780963820..34e0df1ae 100644
--- a/java/dagger/internal/codegen/BindsMethodValidator.java
+++ b/java/dagger/internal/codegen/BindsMethodValidator.java
@@ -23,22 +23,15 @@ import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass
import static dagger.internal.codegen.ErrorMessages.BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET;
import static dagger.internal.codegen.ErrorMessages.BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER;
-import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import dagger.Binds;
import dagger.Module;
import dagger.producers.ProducerModule;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@@ -47,7 +40,7 @@ import javax.lang.model.util.Types;
*/
final class BindsMethodValidator extends BindingMethodValidator {
private final Types types;
- private final Elements elements;
+ private final BindsTypeChecker bindsTypeChecker;
BindsMethodValidator(Elements elements, Types types) {
super(
@@ -59,7 +52,7 @@ final class BindsMethodValidator extends BindingMethodValidator {
RUNTIME_EXCEPTION,
ALLOWS_MULTIBINDINGS);
this.types = types;
- this.elements = elements;
+ this.bindsTypeChecker = new BindsTypeChecker(types, elements);
}
@Override
@@ -76,83 +69,18 @@ final class BindsMethodValidator extends BindingMethodValidator {
TypeMirror leftHandSide = boxIfNecessary(method.getReturnType());
TypeMirror rightHandSide = parameter.asType();
ContributionType contributionType = ContributionType.fromBindingMethod(method);
- switch (contributionType) {
- case SET_VALUES:
- if (!SetType.isSet(leftHandSide)) {
- builder.addError(BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET);
- } else {
- validateTypesAreAssignable(
- builder,
- rightHandSide,
- methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll"));
- }
- break;
- case SET:
- DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
- validateTypesAreAssignable(
- builder,
- rightHandSide,
- methodParameterType(parameterizedSetType, "add"));
- break;
- case MAP:
- DeclaredType parameterizedMapType =
- types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
- validateTypesAreAssignable(
- builder,
- rightHandSide,
- methodParameterTypes(parameterizedMapType, "put").get(1));
- break;
- case UNIQUE:
- validateTypesAreAssignable(builder, rightHandSide, leftHandSide);
- break;
- default:
- throw new AssertionError(
- String.format(
- "Unknown contribution type (%s) for method: %s", contributionType, method));
+ if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
+ builder.addError(BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET);
}
- } else {
- builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
- }
- }
- private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
- ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
- for (ExecutableElement method :
- ElementFilter.methodsIn(MoreElements.asType(type.asElement()).getEnclosedElements())) {
- if (method.getSimpleName().contentEquals(methodName)) {
- methodsForName.add(method);
+ if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
+ builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
}
- }
- ExecutableElement method = getOnlyElement(methodsForName.build());
- return ImmutableList.<TypeMirror>copyOf(
- MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
- }
-
- private TypeMirror methodParameterType(DeclaredType type, String methodName) {
- return getOnlyElement(methodParameterTypes(type, methodName));
- }
-
- private void validateTypesAreAssignable(
- ValidationReport.Builder<ExecutableElement> builder,
- TypeMirror rightHandSide,
- TypeMirror leftHandSide) {
- if (!types.isAssignable(rightHandSide, leftHandSide)) {
+ } else {
builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
}
}
- private TypeElement setElement() {
- return elements.getTypeElement(Set.class.getName());
- }
-
- private TypeElement mapElement() {
- return elements.getTypeElement(Map.class.getName());
- }
-
- private TypeMirror unboundedWildcard() {
- return types.getWildcardType(null, null);
- }
-
private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
if (maybePrimitive.getKind().isPrimitive()) {
return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
diff --git a/java/dagger/internal/codegen/BindsTypeChecker.java b/java/dagger/internal/codegen/BindsTypeChecker.java
new file mode 100644
index 000000000..16855344b
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsTypeChecker.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 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.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Checks the assignability of one type to another, given a {@link ContributionType} context. This
+ * is used by {@link BindsMethodValidator} to validate that the right-hand-side of a {@link
+ * dagger.Binds} method is valid, as well as in {@link DelegateBindingExpression} when the
+ * right-hand-side in generated code might be an erased type due to accessibility.
+ */
+final class BindsTypeChecker {
+ private final Types types;
+ private final Elements elements;
+
+ BindsTypeChecker(Types types, Elements elements) {
+ this.types = types;
+ this.elements = elements;
+ }
+
+ /**
+ * Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link
+ * ContributionType} context.
+ */
+ boolean isAssignable(
+ TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) {
+ return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType));
+ }
+
+ private TypeMirror desiredAssignableType(
+ TypeMirror leftHandSide, ContributionType contributionType) {
+ switch (contributionType) {
+ case UNIQUE:
+ return leftHandSide;
+ case SET:
+ DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
+ return methodParameterType(parameterizedSetType, "add");
+ case SET_VALUES:
+ return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
+ case MAP:
+ DeclaredType parameterizedMapType =
+ types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
+ return methodParameterTypes(parameterizedMapType, "put").get(1);
+ default:
+ throw new AssertionError("Unknown contribution type: " + contributionType);
+ }
+ }
+
+ private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
+ ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
+ for (ExecutableElement method :
+ methodsIn(MoreElements.asType(type.asElement()).getEnclosedElements())) {
+ if (method.getSimpleName().contentEquals(methodName)) {
+ methodsForName.add(method);
+ }
+ }
+ ExecutableElement method = getOnlyElement(methodsForName.build());
+ return ImmutableList.copyOf(
+ MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
+ }
+
+ private TypeMirror methodParameterType(DeclaredType type, String methodName) {
+ return getOnlyElement(methodParameterTypes(type, methodName));
+ }
+
+ private TypeElement setElement() {
+ return elements.getTypeElement(Set.class.getName());
+ }
+
+ private TypeElement mapElement() {
+ return elements.getTypeElement(Map.class.getName());
+ }
+
+ private TypeMirror unboundedWildcard() {
+ return types.getWildcardType(null, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/DelegateBindingExpression.java b/java/dagger/internal/codegen/DelegateBindingExpression.java
new file mode 100644
index 000000000..ad72e5a38
--- /dev/null
+++ b/java/dagger/internal/codegen/DelegateBindingExpression.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.Scope.reusableScope;
+
+import com.squareup.javapoet.ClassName;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+
+/** A {@link BindingExpression} for {@code @Binds} methods. */
+final class DelegateBindingExpression extends BindingExpression {
+ private final ContributionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final Elements elements;
+ private final BindsTypeChecker bindsTypeChecker;
+
+ private DelegateBindingExpression(
+ ResolvedBindings resolvedBindings,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ Elements elements) {
+ super(resolvedBindings);
+ this.binding = checkNotNull(resolvedBindings.contributionBinding());
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+ }
+
+ static BindingExpression create(
+ BindingGraph graph,
+ BindingExpression bindingExpression,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ Elements elements) {
+ ResolvedBindings resolvedBindings = bindingExpression.resolvedBindings();
+ ContributionBinding binding = resolvedBindings.contributionBinding();
+ Binding delegateBinding =
+ graph
+ .resolvedBindings()
+ .get(getOnlyElement(binding.dependencies()).bindingKey())
+ .binding();
+ ScopeKind bindsScope = ScopeKind.get(binding, graph, elements);
+ ScopeKind delegateScope = ScopeKind.get(delegateBinding, graph, elements);
+ if (bindsScope.isSimilarOrWeakerScopeThan(delegateScope)) {
+ return new DelegateBindingExpression(
+ resolvedBindings, componentBindingExpressions, types, elements);
+ }
+ return bindingExpression;
+ }
+
+ @Override
+ Expression getDependencyExpression(
+ DependencyRequest.Kind requestKind, ClassName requestingClass) {
+ Expression delegateExpression =
+ componentBindingExpressions.getDependencyExpression(
+ getOnlyElement(binding.dependencies()).bindingKey(), requestKind, requestingClass);
+
+ TypeMirror contributedType = binding.contributedType();
+ switch (requestKind) {
+ case INSTANCE:
+ return instanceRequiresCast(delegateExpression, requestingClass)
+ ? delegateExpression.castTo(contributedType)
+ : delegateExpression;
+ default:
+ return castToRawTypeIfNecessary(
+ delegateExpression, requestKind.type(contributedType, types));
+ }
+ }
+
+ private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
+ // delegateExpression.type() could be Object if expression is satisfied with a raw
+ // Provider's get() method.
+ return !bindsTypeChecker.isAssignable(
+ delegateExpression.type(), binding.contributedType(), binding.contributionType())
+ && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
+ }
+
+ /**
+ * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
+ * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
+ * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
+ * returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
+ * to the raw type of {@code desiredType}.
+ */
+ // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
+ private Expression castToRawTypeIfNecessary(
+ Expression delegateExpression, TypeMirror desiredType) {
+ if (types.isAssignable(delegateExpression.type(), desiredType)) {
+ return delegateExpression;
+ }
+ return delegateExpression.castTo(types.erasure(desiredType));
+ }
+
+ private enum ScopeKind {
+ UNSCOPED,
+ RELEASABLE,
+ SINGLE_CHECK,
+ DOUBLE_CHECK,
+ ;
+
+ static ScopeKind get(Binding binding, BindingGraph graph, Elements elements) {
+ if (!binding.scope().isPresent()) {
+ return UNSCOPED;
+ }
+
+ Scope scope = binding.scope().get();
+ if (graph.scopesRequiringReleasableReferenceManagers().contains(scope)) {
+ return RELEASABLE;
+ }
+ return scope.equals(reusableScope(elements)) ? SINGLE_CHECK : DOUBLE_CHECK;
+ }
+
+ boolean isSimilarOrWeakerScopeThan(ScopeKind other) {
+ return ordinal() <= other.ordinal();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/DependencyRequest.java b/java/dagger/internal/codegen/DependencyRequest.java
index 06ab062ae..56b152b81 100644
--- a/java/dagger/internal/codegen/DependencyRequest.java
+++ b/java/dagger/internal/codegen/DependencyRequest.java
@@ -161,6 +161,23 @@ abstract class DependencyRequest {
throw new AssertionError(this);
}
}
+
+ /** Returns the type of a request of this kind for the given {@code type}. */
+ TypeMirror type(TypeMirror type, DaggerTypes types) {
+ switch (this) {
+ case INSTANCE:
+ return type;
+
+ case PROVIDER_OF_LAZY:
+ return types.wrapType(LAZY.type(type, types), Provider.class);
+
+ case FUTURE:
+ return types.wrapType(type, ListenableFuture.class);
+
+ default:
+ return types.wrapType(type, frameworkClass.get());
+ }
+ }
}
abstract Kind kind();
diff --git a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
index ac8f3e15f..faec0dc16 100644
--- a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
@@ -107,6 +107,7 @@ final class FrameworkInstanceBindingExpression extends BindingExpression {
maybeInitializeField();
TypeMirror expressionType =
isTypeAccessibleFrom(instanceType(), requestingClass.packageName())
+ || isInlinedFactoryCreation()
? types.wrapType(instanceType(), resolvedBindings().frameworkClass())
: rawFrameworkType();
@@ -128,6 +129,21 @@ final class FrameworkInstanceBindingExpression extends BindingExpression {
.orElseGet(() -> resolvedBindings().contributionBinding().contributedType());
}
+ /**
+ * Returns {@code true} if a factory is created inline each time it is requested. For example, in
+ * the initialization {@code this.fooProvider = Foo_Factory.create(Bar_Factory.create());}, {@code
+ * Bar_Factory} is considered to be inline.
+ *
+ * <p>This is used in {@link #getDependencyExpression(Kind, ClassName)} when determining the type
+ * of a factory. Normally if the {@link #instanceType()} is not accessible from the component, the
+ * type of the expression will be a raw {@link javax.inject.Provider}. However, if the factory is
+ * created inline, even if contributed type is not accessible, javac will still be able to
+ * determine the type that is returned from the {@code Foo_Factory.create()} method.
+ */
+ private boolean isInlinedFactoryCreation() {
+ return memberSelect.staticMember();
+ }
+
private DeclaredType rawFrameworkType() {
return types.getDeclaredType(
elements.getTypeElement(resolvedBindings().frameworkClass().getCanonicalName()));
diff --git a/javatests/dagger/functional/binds/AccessesExposedComponent.java b/javatests/dagger/functional/binds/AccessesExposedComponent.java
new file mode 100644
index 000000000..e43eedb71
--- /dev/null
+++ b/javatests/dagger/functional/binds/AccessesExposedComponent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.binds;
+
+import dagger.Component;
+import dagger.functional.binds.subpackage.Exposed;
+import dagger.functional.binds.subpackage.ExposedModule;
+import dagger.functional.binds.subpackage.UsesExposedInjectsMembers;
+import java.util.List;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+/**
+ * This component tests cases where the right-hand-side of a {@link dagger.Binds} method is not
+ * accessible from the component, but the left-hand-side is. If the right-hand-side is represented
+ * as a Provider (e.g. because it is scoped), then the raw {@code Provider.get()} will return {@link
+ * Object}, which must be downcasted to the type accessible from the component. See {@code
+ * instanceRequiresCast()} in {@link dagger.internal.codegen.DelegateBindingExpression}.
+ */
+@Singleton
+@Component(modules = ExposedModule.class)
+interface AccessesExposedComponent {
+ Exposed exposed();
+ Provider<Exposed> exposedProvider();
+
+ List<? extends Exposed> listOfExposed();
+ Provider<List<? extends Exposed>> providerOfListOfExposed();
+
+ UsesExposedInjectsMembers usesExposedInjectsMembers();
+}
diff --git a/javatests/dagger/functional/binds/subpackage/Exposed.java b/javatests/dagger/functional/binds/subpackage/Exposed.java
new file mode 100644
index 000000000..885aee4a7
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/Exposed.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+public interface Exposed {} \ No newline at end of file
diff --git a/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java
new file mode 100644
index 000000000..074289c0d
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/ExposedInjectsMembers.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+public interface ExposedInjectsMembers {}
diff --git a/javatests/dagger/functional/binds/subpackage/ExposedModule.java b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
new file mode 100644
index 000000000..a2660d95c
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Singleton;
+
+@Module
+public abstract class ExposedModule {
+ @Binds
+ abstract Exposed notExposed(NotExposed notExposed);
+
+ @Provides
+ @Singleton // force a rawtypes Provider
+ static List<NotExposed> notExposedList() {
+ return new ArrayList<>();
+ }
+
+ @Binds
+ abstract List<? extends Exposed> bindList(List<NotExposed> notExposedList);
+
+ @Binds
+ abstract ExposedInjectsMembers bindExposedInjectsMembers(
+ NotExposedInjectsMembers notExposedInjectsMembers);
+}
diff --git a/javatests/dagger/functional/binds/subpackage/NotExposed.java b/javatests/dagger/functional/binds/subpackage/NotExposed.java
new file mode 100644
index 000000000..a8774ee27
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/NotExposed.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton // force a Provider, which will not have a type parameter since this is not public
+class NotExposed implements Exposed {
+ @Inject
+ NotExposed() {}
+} \ No newline at end of file
diff --git a/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java
new file mode 100644
index 000000000..1f6a1b795
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/NotExposedInjectsMembers.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+import javax.inject.Inject;
+
+final class NotExposedInjectsMembers implements ExposedInjectsMembers {
+ @Inject Exposed exposed;
+
+ @Inject NotExposedInjectsMembers() {}
+}
diff --git a/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java b/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java
new file mode 100644
index 000000000..3ee2e8fa9
--- /dev/null
+++ b/javatests/dagger/functional/binds/subpackage/UsesExposedInjectsMembers.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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.binds.subpackage;
+
+import javax.inject.Inject;
+
+public class UsesExposedInjectsMembers {
+ @Inject ExposedInjectsMembers exposedInjectsMembers;
+
+ @Inject UsesExposedInjectsMembers(ExposedInjectsMembers exposedInjectsMembers) {}
+}
diff --git a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
new file mode 100644
index 000000000..1c52ce9ae
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
@@ -0,0 +1,1012 @@
+/*
+ * Copyright (C) 2017 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.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.CompilationSubject;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DelegateBindingExpressionTest {
+ private static final JavaFileObject REGULAR_SCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.RegularScoped",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "import javax.inject.Inject;",
+ "",
+ "@RegularScoped.CustomScope",
+ "class RegularScoped {",
+ " @Inject RegularScoped() {}",
+ "",
+ " @Scope @interface CustomScope {}",
+ "}");
+
+ private static final JavaFileObject REUSABLE_SCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.ReusableScoped",
+ "package test;",
+ "",
+ "import dagger.Reusable;",
+ "import javax.inject.Inject;",
+ "",
+ "@Reusable",
+ "class ReusableScoped {",
+ " @Inject ReusableScoped() {}",
+ "}");
+
+ private static final JavaFileObject RELEASABLE_SCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.ReleasableScoped",
+ "package test;",
+ "",
+ "import dagger.releasablereferences.CanReleaseReferences;",
+ "import javax.inject.Scope;",
+ "import javax.inject.Inject;",
+ "",
+ "@ReleasableScoped.CustomScope",
+ "class ReleasableScoped {",
+ " @Inject ReleasableScoped() {}",
+ "",
+ " @CanReleaseReferences",
+ " @Scope @interface CustomScope {}",
+ "}");
+
+ private static final JavaFileObject UNSCOPED =
+ JavaFileObjects.forSourceLines(
+ "test.Unscoped",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Unscoped {",
+ " @Inject Unscoped() {}",
+ "}");
+
+ private static final JavaFileObject COMPONENT =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.releasablereferences.ForReleasableReferences;",
+ "import dagger.releasablereferences.ReleasableReferenceManager;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "@RegularScoped.CustomScope",
+ "@ReleasableScoped.CustomScope",
+ "interface TestComponent {",
+ " @Qualifier(RegularScoped.class)",
+ " Object regular();",
+ "",
+ " @Qualifier(ReusableScoped.class)",
+ " Object reusable();",
+ "",
+ " @Qualifier(ReleasableScoped.class)",
+ " Object releasable();",
+ "",
+ " @Qualifier(Unscoped.class)",
+ " Object unscoped();",
+ "",
+ // force a reference releasing provider to be created
+ " @ForReleasableReferences(ReleasableScoped.CustomScope.class)",
+ " ReleasableReferenceManager releasableReferenceManager();",
+ "}");
+
+ private static final JavaFileObject QUALIFIER =
+ JavaFileObjects.forSourceLines(
+ "test.Qualifier",
+ "package test;",
+ "",
+ "@javax.inject.Qualifier",
+ "@interface Qualifier {",
+ " Class<?> value();",
+ "}");
+
+ @Test
+ public void toDoubleCheck() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @RegularScoped.CustomScope @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @RegularScoped.CustomScope @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @RegularScoped.CustomScope @Qualifier(ReleasableScoped.class)",
+ " Object releasable(ReleasableScoped delegate);",
+ "",
+ " @Binds @RegularScoped.CustomScope @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.ReferenceReleasingProvider;",
+ "import dagger.internal.ReferenceReleasingProviderManager;",
+ "import dagger.internal.SingleCheck;",
+ "import dagger.releasablereferences.ReleasableReferenceManager;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " private final ReferenceReleasingProviderManager customScopeReferences =",
+ " new ReferenceReleasingProviderManager(ReleasableScoped.CustomScope.class);",
+ " private Provider<RegularScoped> regularScopedProvider;",
+ " private Provider<ReusableScoped> reusableScopedProvider;",
+ " private Provider<Object> reusableProvider;",
+ " private Provider<ReleasableScoped> releasableScopedProvider;",
+ " private Provider<Object> releasableProvider;",
+ " private Provider<Object> unscopedProvider;",
+ " private Provider<ReleasableReferenceManager>",
+ " forReleasableReferencesReleasableReferenceManagerProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.reusableProvider = ",
+ " DoubleCheck.provider((Provider) reusableScopedProvider);",
+ " this.releasableScopedProvider =",
+ " ReferenceReleasingProvider.create(",
+ " ReleasableScoped_Factory.create(), customScopeReferences);",
+ " this.releasableProvider = ",
+ " DoubleCheck.provider((Provider) releasableScopedProvider);",
+ " this.unscopedProvider = ",
+ " DoubleCheck.provider((Provider) Unscoped_Factory.create());",
+ " this.forReleasableReferencesReleasableReferenceManagerProvider =",
+ " new Provider<ReleasableReferenceManager>() {",
+ " @Override",
+ " public ReleasableReferenceManager get() {",
+ " return customScopeReferences;",
+ " }",
+ " };",
+ " }",
+ "",
+ " @Override",
+ " public Object regular() {",
+ " return regularScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object reusable() {",
+ " return reusableProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object releasable() {",
+ " return releasableProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object unscoped() {",
+ " return unscopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public ReleasableReferenceManager releasableReferenceManager() {",
+ " return forReleasableReferencesReleasableReferenceManagerProvider.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void toSingleCheck() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Reusable;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @Reusable @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @Reusable @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @Reusable @Qualifier(ReleasableScoped.class)",
+ " Object releasable(ReleasableScoped delegate);",
+ "",
+ " @Binds @Reusable @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.ReferenceReleasingProvider;",
+ "import dagger.internal.ReferenceReleasingProviderManager;",
+ "import dagger.internal.SingleCheck;",
+ "import dagger.releasablereferences.ReleasableReferenceManager;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " private final ReferenceReleasingProviderManager customScopeReferences =",
+ " new ReferenceReleasingProviderManager(ReleasableScoped.CustomScope.class);",
+ " private Provider<RegularScoped> regularScopedProvider;",
+ " private Provider<ReusableScoped> reusableScopedProvider;",
+ " private Provider<ReleasableScoped> releasableScopedProvider;",
+ " private Provider<Object> releasableProvider;",
+ " private Provider<Object> unscopedProvider;",
+ " private Provider<ReleasableReferenceManager>",
+ " forReleasableReferencesReleasableReferenceManagerProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.releasableScopedProvider = ",
+ " ReferenceReleasingProvider.create(",
+ " ReleasableScoped_Factory.create(), customScopeReferences);",
+ " this.releasableProvider = ",
+ " SingleCheck.provider((Provider) releasableScopedProvider);",
+ " this.unscopedProvider = ",
+ " SingleCheck.provider((Provider) Unscoped_Factory.create());",
+ " this.forReleasableReferencesReleasableReferenceManagerProvider =",
+ " new Provider<ReleasableReferenceManager>() {",
+ " @Override",
+ " public ReleasableReferenceManager get() {",
+ " return customScopeReferences;",
+ " }",
+ " };",
+ " }",
+ "",
+ " @Override",
+ " public Object regular() {",
+ " return regularScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object reusable() {",
+ " return reusableScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object releasable() {",
+ " return releasableProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object unscoped() {",
+ " return unscopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public ReleasableReferenceManager releasableReferenceManager() {",
+ " return forReleasableReferencesReleasableReferenceManagerProvider.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void toReleasableCheck() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @ReleasableScoped.CustomScope @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @ReleasableScoped.CustomScope @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @ReleasableScoped.CustomScope @Qualifier(ReleasableScoped.class)",
+ " Object releasable(ReleasableScoped delegate);",
+ "",
+ " @Binds @ReleasableScoped.CustomScope @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.ReferenceReleasingProvider;",
+ "import dagger.internal.ReferenceReleasingProviderManager;",
+ "import dagger.internal.SingleCheck;",
+ "import dagger.releasablereferences.ReleasableReferenceManager;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " private final ReferenceReleasingProviderManager customScopeReferences =",
+ " new ReferenceReleasingProviderManager(ReleasableScoped.CustomScope.class);",
+ " private Provider<RegularScoped> regularScopedProvider;",
+ " private Provider<ReusableScoped> reusableScopedProvider;",
+ " private Provider<ReleasableScoped> releasableScopedProvider;",
+ " private Provider<Object> unscopedProvider;",
+ " private Provider<ReleasableReferenceManager>",
+ " forReleasableReferencesReleasableReferenceManagerProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.releasableScopedProvider = ",
+ " ReferenceReleasingProvider.create(",
+ " ReleasableScoped_Factory.create(), customScopeReferences);",
+ " this.unscopedProvider = ",
+ " ReferenceReleasingProvider.create(",
+ " (Provider) Unscoped_Factory.create(), customScopeReferences);",
+ " this.forReleasableReferencesReleasableReferenceManagerProvider =",
+ " new Provider<ReleasableReferenceManager>() {",
+ " @Override",
+ " public ReleasableReferenceManager get() {",
+ " return customScopeReferences;",
+ " }",
+ " };",
+ " }",
+ "",
+ " @Override",
+ " public Object regular() {",
+ " return regularScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object reusable() {",
+ " return reusableScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object releasable() {",
+ " return releasableScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object unscoped() {",
+ " return unscopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public ReleasableReferenceManager releasableReferenceManager() {",
+ " return forReleasableReferencesReleasableReferenceManagerProvider.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void toUnscoped() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Binds @Qualifier(RegularScoped.class)",
+ " Object regular(RegularScoped delegate);",
+ "",
+ " @Binds @Qualifier(ReusableScoped.class)",
+ " Object reusable(ReusableScoped delegate);",
+ "",
+ " @Binds @Qualifier(ReleasableScoped.class)",
+ " Object releasable(ReleasableScoped delegate);",
+ "",
+ " @Binds @Qualifier(Unscoped.class)",
+ " Object unscoped(Unscoped delegate);",
+ "}");
+
+ assertThatCompilationWithModule(module)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import dagger.internal.ReferenceReleasingProvider;",
+ "import dagger.internal.ReferenceReleasingProviderManager;",
+ "import dagger.internal.SingleCheck;",
+ "import dagger.releasablereferences.ReleasableReferenceManager;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " private final ReferenceReleasingProviderManager customScopeReferences =",
+ " new ReferenceReleasingProviderManager(ReleasableScoped.CustomScope.class);",
+ " private Provider<RegularScoped> regularScopedProvider;",
+ " private Provider<ReusableScoped> reusableScopedProvider;",
+ " private Provider<ReleasableScoped> releasableScopedProvider;",
+ " private Provider<ReleasableReferenceManager>",
+ " forReleasableReferencesReleasableReferenceManagerProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.regularScopedProvider = ",
+ " DoubleCheck.provider(RegularScoped_Factory.create());",
+ " this.reusableScopedProvider = ",
+ " SingleCheck.provider(ReusableScoped_Factory.create());",
+ " this.releasableScopedProvider = ",
+ " ReferenceReleasingProvider.create(",
+ " ReleasableScoped_Factory.create(), customScopeReferences);",
+ " this.forReleasableReferencesReleasableReferenceManagerProvider =",
+ " new Provider<ReleasableReferenceManager>() {",
+ " @Override",
+ " public ReleasableReferenceManager get() {",
+ " return customScopeReferences;",
+ " }",
+ " };",
+ " }",
+ "",
+ " @Override",
+ " public Object regular() {",
+ " return regularScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object reusable() {",
+ " return reusableScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object releasable() {",
+ " return releasableScopedProvider.get();",
+ " }",
+ "",
+ " @Override",
+ " public Object unscoped() {",
+ " return new Unscoped();",
+ " }",
+ "",
+ " @Override",
+ " public ReleasableReferenceManager releasableReferenceManager() {",
+ " return forReleasableReferencesReleasableReferenceManagerProvider.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void castNeeded_rawTypes_Provider_get() {
+ JavaFileObject accessibleSupertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype",
+ "package other;",
+ "",
+ // accessible from the component, but the subtype is not
+ "public interface Supertype {}");
+ JavaFileObject inaccessibleSubtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Subtype implements Supertype {",
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.SupertypeModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface SupertypeModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = other.SupertypeModule.class)",
+ "interface TestComponent {",
+ " other.Supertype supertype();",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(accessibleSupertype, inaccessibleSubtype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "import other.Subtype_Factory;",
+ "import other.Supertype;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider subtypeProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());",
+ " }",
+ "",
+ " @Override",
+ " public Supertype supertype() {",
+ " return (Supertype) subtypeProvider.get();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void noCast_rawTypes_Provider_get_toInaccessibleType() {
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype",
+ "package other;",
+ "",
+ "interface Supertype {}");
+ JavaFileObject subtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Subtype implements Supertype {",
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject usesSupertype =
+ JavaFileObjects.forSourceLines(
+ "other.UsesSupertype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "public class UsesSupertype {",
+ " @Inject UsesSupertype(Supertype supertype) {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.SupertypeModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface SupertypeModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component(modules = other.SupertypeModule.class)",
+ "interface TestComponent {",
+ " other.UsesSupertype usesSupertype();",
+ "}");
+ Compilation compilation =
+ daggerCompiler().compile(supertype, subtype, usesSupertype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "package test;",
+ "",
+ "import dagger.internal.DoubleCheck;",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "import other.Subtype_Factory;",
+ "import other.UsesSupertype;",
+ "import other.UsesSupertype_Factory;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private Provider subtypeProvider;",
+ "",
+ " private DaggerTestComponent(Builder builder) {",
+ " initialize(builder);",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize(final Builder builder) {",
+ " this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());",
+ " }",
+ "",
+ " @Override",
+ " public UsesSupertype usesSupertype() {",
+ // can't cast the provider.get() to a type that's not accessible
+ " return UsesSupertype_Factory.newUsesSupertype(subtypeProvider.get());",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void castedToRawType() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Module",
+ "interface TestModule {",
+ " @Provides",
+ " static String provideString() { return new String(); }",
+ "",
+ " @Binds",
+ " CharSequence charSequence(String string);",
+ "",
+ " @Binds",
+ " Object object(CharSequence charSequence);",
+ "",
+ " @Binds",
+ " @Named(\"named\")",
+ " String namedString(String string);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " Provider<CharSequence> charSequence();",
+ " Provider<Object> object();",
+ "",
+ " @Named(\"named\") Provider<String> namedString();",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerTestComponent",
+ "",
+ "package test;",
+ "",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ GENERATED_ANNOTATION,
+ "public final class DaggerTestComponent implements TestComponent {",
+ " private DaggerTestComponent(Builder builder) {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static TestComponent create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<CharSequence> charSequence() {",
+ " return (Provider) TestModule_ProvideStringFactory.create();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Object> object() {",
+ // @Binds used twice, but no need to cast (Provider) (Provider)
+ " return (Provider) TestModule_ProvideStringFactory.create();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<String> namedString() {",
+ " return TestModule_ProvideStringFactory.create();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public TestComponent build() {",
+ " return new DaggerTestComponent(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void inlineFactoryOfInacessibleType() {
+ JavaFileObject supertype =
+ JavaFileObjects.forSourceLines(
+ "other.Supertype", "package other;", "", "public interface Supertype {}");
+ JavaFileObject injectableSubtype =
+ JavaFileObjects.forSourceLines(
+ "other.Subtype",
+ "package other;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "final class Subtype implements Supertype {",
+ // important: this doesn't have any dependencies and therefore the factory will be able
+ // to be referenced with an inline Subtype_Factory.create()
+ " @Inject Subtype() {}",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "other.TestModule",
+ "package other;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "public interface TestModule {",
+ " @Binds Supertype to(Subtype subtype);",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.RequestsSubtypeAsProvider",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component(modules = other.TestModule.class)",
+ "interface RequestsSubtypeAsProvider {",
+ " Provider<other.Supertype> supertypeProvider();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(supertype, injectableSubtype, module, component);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerRequestsSubtypeAsProvider")
+ .hasSourceEquivalentTo(
+ JavaFileObjects.forSourceLines(
+ "test.DaggerRequestsSubtypeAsProvider",
+ "package test;",
+ "",
+ "import javax.annotation.Generated;",
+ "import javax.inject.Provider;",
+ "import other.Subtype_Factory;",
+ "import other.Supertype;",
+ "",
+ GENERATED_ANNOTATION,
+ "public final class DaggerRequestsSubtypeAsProvider ",
+ " implements RequestsSubtypeAsProvider {",
+ " private DaggerRequestsSubtypeAsProvider(Builder builder) {}",
+ "",
+ " public static Builder builder() {",
+ " return new Builder();",
+ " }",
+ "",
+ " public static RequestsSubtypeAsProvider create() {",
+ " return new Builder().build();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Supertype> supertypeProvider() {",
+ " return (Provider) Subtype_Factory.create();",
+ " }",
+ "",
+ " public static final class Builder {",
+ " private Builder() {}",
+ "",
+ " public RequestsSubtypeAsProvider build() {",
+ " return new DaggerRequestsSubtypeAsProvider(this);",
+ " }",
+ " }",
+ "}"));
+ }
+
+ private CompilationSubject assertThatCompilationWithModule(JavaFileObject module) {
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ module,
+ COMPONENT,
+ QUALIFIER,
+ REGULAR_SCOPED,
+ REUSABLE_SCOPED,
+ RELEASABLE_SCOPED,
+ UNSCOPED);
+ assertThat(compilation).succeeded();
+ return assertThat(compilation);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index 15d3a3b2c..4299abdc9 100644
--- a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -457,9 +457,6 @@ public class MapBindingComponentProcessorTest {
" @SuppressWarnings(\"rawtypes\")",
" private Provider mapOfPackagePrivateEnumAndIntegerProvider;",
"",
- " private Provider<Object>",
- " bindInaccessibleEnumMapToAccessibleTypeForComponentProvider;",
- "",
" private Provider<Map<MapKeys.ComplexKey, Integer>>",
" mapOfComplexKeyAndIntegerProvider;",
"",
@@ -487,8 +484,6 @@ public class MapBindingComponentProcessorTest {
" .put(MapModule_EnumKeyFactory.mapKey(), ",
" (Provider) MapModule_EnumKeyFactory.create())",
" .build();",
- " this.bindInaccessibleEnumMapToAccessibleTypeForComponentProvider =",
- " (Provider) mapOfPackagePrivateEnumAndIntegerProvider;",
" this.mapOfComplexKeyAndIntegerProvider =",
" MapFactory.<MapKeys.ComplexKey, Integer>builder(3)",
" .put(",
@@ -516,12 +511,13 @@ public class MapBindingComponentProcessorTest {
"",
" @Override",
" public Object inaccessibleEnum() {",
- " return bindInaccessibleEnumMapToAccessibleTypeForComponentProvider.get();",
+ " return ImmutableMap.of(",
+ " MapModule_EnumKeyFactory.mapKey(), MapModule.enumKey());",
" }",
"",
" @Override",
" public Provider<Object> inaccessibleEnumProvider() {",
- " return bindInaccessibleEnumMapToAccessibleTypeForComponentProvider;",
+ " return mapOfPackagePrivateEnumAndIntegerProvider;",
" }",
"",
" @Override",