• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.hilt.processor.internal.root;
18 
19 import static com.google.common.base.Preconditions.checkState;
20 import static com.google.common.base.Suppliers.memoize;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
22 
23 import androidx.room.compiler.processing.XConstructorElement;
24 import androidx.room.compiler.processing.XProcessingEnv;
25 import androidx.room.compiler.processing.XTypeElement;
26 import com.google.common.base.Supplier;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.ImmutableSetMultimap;
29 import com.squareup.javapoet.ClassName;
30 import com.squareup.javapoet.TypeName;
31 import dagger.hilt.processor.internal.ClassNames;
32 import dagger.hilt.processor.internal.ComponentDescriptor;
33 import dagger.hilt.processor.internal.Processors;
34 import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies;
35 import dagger.hilt.processor.internal.aliasof.AliasOfs;
36 import java.util.List;
37 import javax.lang.model.element.TypeElement;
38 import javax.tools.Diagnostic;
39 
40 /** Contains metadata about the given hilt root. */
41 public final class RootMetadata {
42 
43   private static final ClassName APPLICATION_CONTEXT_MODULE =
44       ClassName.get("dagger.hilt.android.internal.modules", "ApplicationContextModule");
45 
create( Root root, ComponentTree componentTree, ComponentDependencies deps, AliasOfs aliasOfs, XProcessingEnv env)46   static RootMetadata create(
47       Root root,
48       ComponentTree componentTree,
49       ComponentDependencies deps,
50       AliasOfs aliasOfs,
51       XProcessingEnv env) {
52     return createInternal(root, componentTree, deps, aliasOfs, env);
53   }
54 
copyWithNewTree(RootMetadata other, ComponentTree componentTree)55   static RootMetadata copyWithNewTree(RootMetadata other, ComponentTree componentTree) {
56     return createInternal(other.root, componentTree, other.deps, other.aliasOfs, other.env);
57   }
58 
createInternal( Root root, ComponentTree componentTree, ComponentDependencies deps, AliasOfs aliasOfs, XProcessingEnv env)59   private static RootMetadata createInternal(
60       Root root,
61       ComponentTree componentTree,
62       ComponentDependencies deps,
63       AliasOfs aliasOfs,
64       XProcessingEnv env) {
65     RootMetadata metadata = new RootMetadata(root, componentTree, deps, aliasOfs, env);
66     metadata.validate();
67     return metadata;
68   }
69 
70   private final Root root;
71   private final XProcessingEnv env;
72   private final ComponentTree componentTree;
73   private final ComponentDependencies deps;
74   private final AliasOfs aliasOfs;
75   private final Supplier<ImmutableSetMultimap<ClassName, ClassName>> scopesByComponent =
76       memoize(this::getScopesByComponentUncached);
77   private final Supplier<TestRootMetadata> testRootMetadata =
78       memoize(this::testRootMetadataUncached);
79 
RootMetadata( Root root, ComponentTree componentTree, ComponentDependencies deps, AliasOfs aliasOfs, XProcessingEnv env)80   private RootMetadata(
81       Root root,
82       ComponentTree componentTree,
83       ComponentDependencies deps,
84       AliasOfs aliasOfs,
85       XProcessingEnv env) {
86     this.root = root;
87     this.env = env;
88     this.componentTree = componentTree;
89     this.deps = deps;
90     this.aliasOfs = aliasOfs;
91   }
92 
root()93   public Root root() {
94     return root;
95   }
96 
componentTree()97   public ComponentTree componentTree() {
98     return componentTree;
99   }
100 
deps()101   public ComponentDependencies deps() {
102     return deps;
103   }
104 
modules(ClassName componentName)105   public ImmutableSet<XTypeElement> modules(ClassName componentName) {
106     return deps.modules().get(componentName).stream().collect(toImmutableSet());
107   }
108 
entryPoints(ClassName componentName)109   public ImmutableSet<TypeName> entryPoints(ClassName componentName) {
110     return ImmutableSet.<TypeName>builder()
111         .addAll(
112             deps.entryPoints().get(componentName).stream()
113                 .map(XTypeElement::getClassName)
114                 .collect(toImmutableSet()))
115         .add(
116             root.isTestRoot() && componentName.equals(ClassNames.SINGLETON_COMPONENT)
117                 ? ClassNames.TEST_SINGLETON_COMPONENT
118                 : ClassNames.GENERATED_COMPONENT)
119         .add(componentName)
120         .build();
121   }
122 
scopes(ClassName componentName)123   public ImmutableSet<ClassName> scopes(ClassName componentName) {
124     return scopesByComponent.get().get(componentName);
125   }
126 
127   /**
128    * Returns all modules in the given component that do not have accessible default constructors.
129    * Note that a non-static module nested in an outer class is considered to have no default
130    * constructors, since an instance of the outer class is needed to construct the module. This also
131    * filters out framework modules directly referenced by the codegen, since those are already known
132    * about and are specifically handled in the codegen.
133    */
modulesThatDaggerCannotConstruct(ClassName componentName)134   public ImmutableSet<XTypeElement> modulesThatDaggerCannotConstruct(ClassName componentName) {
135     return modules(componentName).stream()
136         .filter(module -> !daggerCanConstruct(module))
137         .filter(module -> !APPLICATION_CONTEXT_MODULE.equals(module.getClassName()))
138         .collect(toImmutableSet());
139   }
140 
testRootMetadata()141   public TestRootMetadata testRootMetadata() {
142     checkState(!root.isDefaultRoot(), "The default root does not have TestRootMetadata!");
143     return testRootMetadata.get();
144   }
145 
waitForBindValue()146   public boolean waitForBindValue() {
147     return false;
148   }
149 
testRootMetadataUncached()150   private TestRootMetadata testRootMetadataUncached() {
151     return TestRootMetadata.of(env, root().element());
152   }
153 
154   /**
155    * Validates that the {@link RootType} annotation is compatible with its {@link TypeElement} and
156    * {@link ComponentDependencies}.
157    */
validate()158   private void validate() {
159 
160     // Only test modules in the application component can be missing default constructor
161     for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
162       ClassName componentName = componentDescriptor.component();
163       for (XTypeElement extraModule : modulesThatDaggerCannotConstruct(componentName)) {
164         if (root.isTestRoot() && !componentName.equals(ClassNames.SINGLETON_COMPONENT)) {
165           env.getMessager()
166               .printMessage(
167                   Diagnostic.Kind.ERROR,
168                   "[Hilt] All test modules (unless installed in ApplicationComponent) must use "
169                       + "static provision methods or have a visible, no-arg constructor. Found: "
170                       + extraModule.getQualifiedName(),
171                   root.originatingRootElement());
172         } else if (!root.isTestRoot()) {
173           env.getMessager()
174               .printMessage(
175                   Diagnostic.Kind.ERROR,
176                   "[Hilt] All modules must be static and use static provision methods or have a "
177                       + "visible, no-arg constructor. Found: "
178                       + extraModule.getQualifiedName(),
179                   root.originatingRootElement());
180         }
181       }
182     }
183   }
184 
getScopesByComponentUncached()185   private ImmutableSetMultimap<ClassName, ClassName> getScopesByComponentUncached() {
186     ImmutableSetMultimap.Builder<ClassName, ClassName> builder = ImmutableSetMultimap.builder();
187     for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
188       for (ClassName scope : componentDescriptor.scopes()) {
189         builder.put(componentDescriptor.component(), scope);
190         builder.putAll(componentDescriptor.component(), aliasOfs.getAliasesFor(scope));
191       }
192     }
193 
194     return builder.build();
195   }
196 
daggerCanConstruct(XTypeElement type)197   private static boolean daggerCanConstruct(XTypeElement type) {
198     if (!Processors.requiresModuleInstance(type)) {
199       return true;
200     }
201     return hasVisibleEmptyConstructor(type) && (!type.isNested() || type.isStatic());
202   }
203 
hasVisibleEmptyConstructor(XTypeElement type)204   private static boolean hasVisibleEmptyConstructor(XTypeElement type) {
205     List<XConstructorElement> constructors = type.getConstructors();
206     return constructors.isEmpty()
207         || constructors.stream()
208             .filter(constructor -> constructor.getParameters().isEmpty())
209             .anyMatch(constructor -> !constructor.isPrivate());
210   }
211 }
212