• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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;
18 
19 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
20 
21 import androidx.room.compiler.processing.XElement;
22 import androidx.room.compiler.processing.XProcessingEnv;
23 import androidx.room.compiler.processing.XProcessingStep;
24 import androidx.room.compiler.processing.XRoundEnv;
25 import com.google.common.collect.ImmutableSet;
26 import com.squareup.javapoet.ClassName;
27 import java.util.Map;
28 import java.util.Set;
29 
30 /**
31  * Implements default configurations for ProcessingSteps, and provides structure for exception
32  * handling.
33  *
34  * <p>In each round it will do the following:
35  *
36  * <ol>
37  *   <li>#preProcess()
38  *   <li>foreach element:
39  *       <ul>
40  *         <li>#processEach()
41  *       </ul>
42  *   <li>#postProcess()
43  * </ol>
44  *
45  * <p>#processEach() allows each element to be processed, even if exceptions are thrown. Due to the
46  * non-deterministic ordering of the processed elements, this is needed to ensure a consistent set
47  * of exceptions are thrown with each build.
48  */
49 public abstract class BaseProcessingStep implements XProcessingStep {
50   private final ProcessorErrorHandler errorHandler;
51   boolean isAnnotationClassNamesOverridden = true;
52 
53   private final XProcessingEnv processingEnv;
54 
BaseProcessingStep(XProcessingEnv env)55   public BaseProcessingStep(XProcessingEnv env) {
56     errorHandler = new ProcessorErrorHandler(env);
57     processingEnv = env;
58   }
59 
processingEnv()60   protected final XProcessingEnv processingEnv() {
61     return processingEnv;
62   }
63 
64   @Override
annotations()65   public final ImmutableSet<String> annotations() {
66     ImmutableSet<ClassName> annotationClassNames = annotationClassNames();
67     if (!isAnnotationClassNamesOverridden) {
68       return ImmutableSet.of("*");
69     }
70     if (annotationClassNames == null || annotationClassNames.isEmpty()) {
71       throw new IllegalStateException("annotationClassNames() should return one or more elements.");
72     } else {
73       return annotationClassNames.stream().map(ClassName::canonicalName).collect(toImmutableSet());
74     }
75   }
76 
77   // When this method is not implemented by users, all annotated elements will processed by this
78   // processing step.
annotationClassNames()79   protected ImmutableSet<ClassName> annotationClassNames() {
80     isAnnotationClassNamesOverridden = false;
81     return ImmutableSet.of();
82   }
83 
processEach(ClassName annotation, XElement element)84   protected void processEach(ClassName annotation, XElement element) throws Exception {}
85 
preProcess(XProcessingEnv env, XRoundEnv round)86   protected void preProcess(XProcessingEnv env, XRoundEnv round) {}
87 
postProcess(XProcessingEnv env, XRoundEnv round)88   protected void postProcess(XProcessingEnv env, XRoundEnv round) throws Exception {}
89 
preRoundProcess(XProcessingEnv env, XRoundEnv round)90   public final void preRoundProcess(XProcessingEnv env, XRoundEnv round) {
91     preProcess(env, round);
92   }
93 
postRoundProcess(XProcessingEnv env, XRoundEnv round)94   public final void postRoundProcess(XProcessingEnv env, XRoundEnv round) {
95     if (errorHandler.isEmpty()) {
96       try {
97         postProcess(env, round);
98       } catch (Exception e) {
99         errorHandler.recordError(e);
100       }
101     }
102     if (!delayErrors() || round.isProcessingOver()) {
103       errorHandler.checkErrors();
104     }
105   }
106 
107   @Override
process( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation, boolean isLastRound)108   public final ImmutableSet<XElement> process(
109       XProcessingEnv env,
110       Map<String, ? extends Set<? extends XElement>> elementsByAnnotation,
111       boolean isLastRound) {
112     ImmutableSet.Builder<XElement> elementsToReprocessBuilder = ImmutableSet.builder();
113     for (ClassName annotationName : annotationClassNames()) {
114       Set<? extends XElement> elements = elementsByAnnotation.get(annotationName.canonicalName());
115       if (elements != null) {
116         for (XElement element : elements) {
117           try {
118             processEach(annotationName, element);
119           } catch (Exception e) {
120             if (e instanceof ErrorTypeException && !isLastRound) {
121               // Allow an extra round to reprocess to try to resolve this type.
122               elementsToReprocessBuilder.add(element);
123             } else {
124               errorHandler.recordError(e);
125             }
126           }
127         }
128       }
129     }
130     return elementsToReprocessBuilder.build();
131   }
132 
133   /**
134    * Returns true if you want to delay errors to the last round. Useful if the processor generates
135    * code for symbols used a lot in the user code. Delaying allows as much code to compile as
136    * possible for correctly configured types and reduces error spam.
137    */
delayErrors()138   protected boolean delayErrors() {
139     return false;
140   }
141 }
142