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