diff options
author | ronshapiro <ronshapiro@google.com> | 2017-10-09 13:24:09 -0700 |
---|---|---|
committer | Ron Shapiro <shapiro.rd@gmail.com> | 2017-10-09 17:59:15 -0400 |
commit | e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8 (patch) | |
tree | c326d6623dc471564e723e1f987e80189014d0b4 | |
parent | 6d1e6acc5fd8ac20d2cf83d20e3a9ed946f75943 (diff) | |
download | dagger2-upstream/dagger-2.12.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
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", |