Skip to content

Commit e8d7cd4

Browse files
committed
Inline requests for @BINDS bindings
This is a rollforward of e2bff35 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
1 parent 6d1e6ac commit e8d7cd4

15 files changed

+1502
-86
lines changed

java/dagger/internal/codegen/BindingExpression.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ private BindingExpression create(
217217
return new OptionalBindingExpression(
218218
provisionBinding, bindingExpression, componentBindingExpressions, types);
219219

220+
case SYNTHETIC_DELEGATE_BINDING:
221+
return DelegateBindingExpression.create(
222+
graph, bindingExpression, componentBindingExpressions, types, elements);
223+
220224
case BUILDER_BINDING:
221225
return new BoundInstanceBindingExpression(
222226
bindingExpression,

java/dagger/internal/codegen/BindsMethodValidator.java

Lines changed: 7 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,15 @@
2323
import static dagger.internal.codegen.ErrorMessages.BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET;
2424
import static dagger.internal.codegen.ErrorMessages.BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER;
2525

26-
import com.google.auto.common.MoreElements;
2726
import com.google.auto.common.MoreTypes;
28-
import com.google.common.collect.ImmutableList;
2927
import com.google.common.collect.ImmutableSet;
3028
import dagger.Binds;
3129
import dagger.Module;
3230
import dagger.producers.ProducerModule;
3331
import java.util.List;
34-
import java.util.Map;
35-
import java.util.Set;
3632
import javax.lang.model.element.ExecutableElement;
37-
import javax.lang.model.element.TypeElement;
3833
import javax.lang.model.element.VariableElement;
39-
import javax.lang.model.type.DeclaredType;
4034
import javax.lang.model.type.TypeMirror;
41-
import javax.lang.model.util.ElementFilter;
4235
import javax.lang.model.util.Elements;
4336
import javax.lang.model.util.Types;
4437

@@ -47,7 +40,7 @@
4740
*/
4841
final class BindsMethodValidator extends BindingMethodValidator {
4942
private final Types types;
50-
private final Elements elements;
43+
private final BindsTypeChecker bindsTypeChecker;
5144

5245
BindsMethodValidator(Elements elements, Types types) {
5346
super(
@@ -59,7 +52,7 @@ final class BindsMethodValidator extends BindingMethodValidator {
5952
RUNTIME_EXCEPTION,
6053
ALLOWS_MULTIBINDINGS);
6154
this.types = types;
62-
this.elements = elements;
55+
this.bindsTypeChecker = new BindsTypeChecker(types, elements);
6356
}
6457

6558
@Override
@@ -76,83 +69,18 @@ private void checkParameters(ValidationReport.Builder<ExecutableElement> builder
7669
TypeMirror leftHandSide = boxIfNecessary(method.getReturnType());
7770
TypeMirror rightHandSide = parameter.asType();
7871
ContributionType contributionType = ContributionType.fromBindingMethod(method);
79-
switch (contributionType) {
80-
case SET_VALUES:
81-
if (!SetType.isSet(leftHandSide)) {
82-
builder.addError(BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET);
83-
} else {
84-
validateTypesAreAssignable(
85-
builder,
86-
rightHandSide,
87-
methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll"));
88-
}
89-
break;
90-
case SET:
91-
DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
92-
validateTypesAreAssignable(
93-
builder,
94-
rightHandSide,
95-
methodParameterType(parameterizedSetType, "add"));
96-
break;
97-
case MAP:
98-
DeclaredType parameterizedMapType =
99-
types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
100-
validateTypesAreAssignable(
101-
builder,
102-
rightHandSide,
103-
methodParameterTypes(parameterizedMapType, "put").get(1));
104-
break;
105-
case UNIQUE:
106-
validateTypesAreAssignable(builder, rightHandSide, leftHandSide);
107-
break;
108-
default:
109-
throw new AssertionError(
110-
String.format(
111-
"Unknown contribution type (%s) for method: %s", contributionType, method));
72+
if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
73+
builder.addError(BINDS_ELEMENTS_INTO_SET_METHOD_RETURN_SET);
11274
}
113-
} else {
114-
builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
115-
}
116-
}
11775

118-
private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
119-
ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
120-
for (ExecutableElement method :
121-
ElementFilter.methodsIn(MoreElements.asType(type.asElement()).getEnclosedElements())) {
122-
if (method.getSimpleName().contentEquals(methodName)) {
123-
methodsForName.add(method);
76+
if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
77+
builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
12478
}
125-
}
126-
ExecutableElement method = getOnlyElement(methodsForName.build());
127-
return ImmutableList.<TypeMirror>copyOf(
128-
MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
129-
}
130-
131-
private TypeMirror methodParameterType(DeclaredType type, String methodName) {
132-
return getOnlyElement(methodParameterTypes(type, methodName));
133-
}
134-
135-
private void validateTypesAreAssignable(
136-
ValidationReport.Builder<ExecutableElement> builder,
137-
TypeMirror rightHandSide,
138-
TypeMirror leftHandSide) {
139-
if (!types.isAssignable(rightHandSide, leftHandSide)) {
79+
} else {
14080
builder.addError(BINDS_METHOD_ONE_ASSIGNABLE_PARAMETER);
14181
}
14282
}
14383

144-
private TypeElement setElement() {
145-
return elements.getTypeElement(Set.class.getName());
146-
}
147-
148-
private TypeElement mapElement() {
149-
return elements.getTypeElement(Map.class.getName());
150-
}
151-
152-
private TypeMirror unboundedWildcard() {
153-
return types.getWildcardType(null, null);
154-
}
155-
15684
private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
15785
if (maybePrimitive.getKind().isPrimitive()) {
15886
return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (C) 2017 The Dagger Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package dagger.internal.codegen;
18+
19+
import static com.google.common.collect.Iterables.getOnlyElement;
20+
import static javax.lang.model.util.ElementFilter.methodsIn;
21+
22+
import com.google.auto.common.MoreElements;
23+
import com.google.auto.common.MoreTypes;
24+
import com.google.common.collect.ImmutableList;
25+
import java.util.Map;
26+
import java.util.Set;
27+
import javax.lang.model.element.ExecutableElement;
28+
import javax.lang.model.element.TypeElement;
29+
import javax.lang.model.type.DeclaredType;
30+
import javax.lang.model.type.TypeMirror;
31+
import javax.lang.model.util.Elements;
32+
import javax.lang.model.util.Types;
33+
34+
/**
35+
* Checks the assignability of one type to another, given a {@link ContributionType} context. This
36+
* is used by {@link BindsMethodValidator} to validate that the right-hand-side of a {@link
37+
* dagger.Binds} method is valid, as well as in {@link DelegateBindingExpression} when the
38+
* right-hand-side in generated code might be an erased type due to accessibility.
39+
*/
40+
final class BindsTypeChecker {
41+
private final Types types;
42+
private final Elements elements;
43+
44+
BindsTypeChecker(Types types, Elements elements) {
45+
this.types = types;
46+
this.elements = elements;
47+
}
48+
49+
/**
50+
* Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link
51+
* ContributionType} context.
52+
*/
53+
boolean isAssignable(
54+
TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) {
55+
return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType));
56+
}
57+
58+
private TypeMirror desiredAssignableType(
59+
TypeMirror leftHandSide, ContributionType contributionType) {
60+
switch (contributionType) {
61+
case UNIQUE:
62+
return leftHandSide;
63+
case SET:
64+
DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
65+
return methodParameterType(parameterizedSetType, "add");
66+
case SET_VALUES:
67+
return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
68+
case MAP:
69+
DeclaredType parameterizedMapType =
70+
types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
71+
return methodParameterTypes(parameterizedMapType, "put").get(1);
72+
default:
73+
throw new AssertionError("Unknown contribution type: " + contributionType);
74+
}
75+
}
76+
77+
private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
78+
ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
79+
for (ExecutableElement method :
80+
methodsIn(MoreElements.asType(type.asElement()).getEnclosedElements())) {
81+
if (method.getSimpleName().contentEquals(methodName)) {
82+
methodsForName.add(method);
83+
}
84+
}
85+
ExecutableElement method = getOnlyElement(methodsForName.build());
86+
return ImmutableList.copyOf(
87+
MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
88+
}
89+
90+
private TypeMirror methodParameterType(DeclaredType type, String methodName) {
91+
return getOnlyElement(methodParameterTypes(type, methodName));
92+
}
93+
94+
private TypeElement setElement() {
95+
return elements.getTypeElement(Set.class.getName());
96+
}
97+
98+
private TypeElement mapElement() {
99+
return elements.getTypeElement(Map.class.getName());
100+
}
101+
102+
private TypeMirror unboundedWildcard() {
103+
return types.getWildcardType(null, null);
104+
}
105+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright (C) 2017 The Dagger Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package dagger.internal.codegen;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
import static com.google.common.collect.Iterables.getOnlyElement;
21+
import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
22+
import static dagger.internal.codegen.Scope.reusableScope;
23+
24+
import com.squareup.javapoet.ClassName;
25+
import javax.lang.model.type.TypeMirror;
26+
import javax.lang.model.util.Elements;
27+
28+
/** A {@link BindingExpression} for {@code @Binds} methods. */
29+
final class DelegateBindingExpression extends BindingExpression {
30+
private final ContributionBinding binding;
31+
private final ComponentBindingExpressions componentBindingExpressions;
32+
private final DaggerTypes types;
33+
private final Elements elements;
34+
private final BindsTypeChecker bindsTypeChecker;
35+
36+
private DelegateBindingExpression(
37+
ResolvedBindings resolvedBindings,
38+
ComponentBindingExpressions componentBindingExpressions,
39+
DaggerTypes types,
40+
Elements elements) {
41+
super(resolvedBindings);
42+
this.binding = checkNotNull(resolvedBindings.contributionBinding());
43+
this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
44+
this.types = checkNotNull(types);
45+
this.elements = checkNotNull(elements);
46+
this.bindsTypeChecker = new BindsTypeChecker(types, elements);
47+
}
48+
49+
static BindingExpression create(
50+
BindingGraph graph,
51+
BindingExpression bindingExpression,
52+
ComponentBindingExpressions componentBindingExpressions,
53+
DaggerTypes types,
54+
Elements elements) {
55+
ResolvedBindings resolvedBindings = bindingExpression.resolvedBindings();
56+
ContributionBinding binding = resolvedBindings.contributionBinding();
57+
Binding delegateBinding =
58+
graph
59+
.resolvedBindings()
60+
.get(getOnlyElement(binding.dependencies()).bindingKey())
61+
.binding();
62+
ScopeKind bindsScope = ScopeKind.get(binding, graph, elements);
63+
ScopeKind delegateScope = ScopeKind.get(delegateBinding, graph, elements);
64+
if (bindsScope.isSimilarOrWeakerScopeThan(delegateScope)) {
65+
return new DelegateBindingExpression(
66+
resolvedBindings, componentBindingExpressions, types, elements);
67+
}
68+
return bindingExpression;
69+
}
70+
71+
@Override
72+
Expression getDependencyExpression(
73+
DependencyRequest.Kind requestKind, ClassName requestingClass) {
74+
Expression delegateExpression =
75+
componentBindingExpressions.getDependencyExpression(
76+
getOnlyElement(binding.dependencies()).bindingKey(), requestKind, requestingClass);
77+
78+
TypeMirror contributedType = binding.contributedType();
79+
switch (requestKind) {
80+
case INSTANCE:
81+
return instanceRequiresCast(delegateExpression, requestingClass)
82+
? delegateExpression.castTo(contributedType)
83+
: delegateExpression;
84+
default:
85+
return castToRawTypeIfNecessary(
86+
delegateExpression, requestKind.type(contributedType, types));
87+
}
88+
}
89+
90+
private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
91+
// delegateExpression.type() could be Object if expression is satisfied with a raw
92+
// Provider's get() method.
93+
return !bindsTypeChecker.isAssignable(
94+
delegateExpression.type(), binding.contributedType(), binding.contributionType())
95+
&& isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
96+
}
97+
98+
/**
99+
* If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
100+
* delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
101+
* type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
102+
* returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
103+
* to the raw type of {@code desiredType}.
104+
*/
105+
// TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
106+
private Expression castToRawTypeIfNecessary(
107+
Expression delegateExpression, TypeMirror desiredType) {
108+
if (types.isAssignable(delegateExpression.type(), desiredType)) {
109+
return delegateExpression;
110+
}
111+
return delegateExpression.castTo(types.erasure(desiredType));
112+
}
113+
114+
private enum ScopeKind {
115+
UNSCOPED,
116+
RELEASABLE,
117+
SINGLE_CHECK,
118+
DOUBLE_CHECK,
119+
;
120+
121+
static ScopeKind get(Binding binding, BindingGraph graph, Elements elements) {
122+
if (!binding.scope().isPresent()) {
123+
return UNSCOPED;
124+
}
125+
126+
Scope scope = binding.scope().get();
127+
if (graph.scopesRequiringReleasableReferenceManagers().contains(scope)) {
128+
return RELEASABLE;
129+
}
130+
return scope.equals(reusableScope(elements)) ? SINGLE_CHECK : DOUBLE_CHECK;
131+
}
132+
133+
boolean isSimilarOrWeakerScopeThan(ScopeKind other) {
134+
return ordinal() <= other.ordinal();
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)