1 /* 2 * Copyright (C) 2019 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 com.google.auto.common.MoreTypes; 20 import com.google.common.base.Preconditions; 21 import com.google.common.collect.ImmutableList; 22 import com.google.errorprone.annotations.FormatMethod; 23 import com.google.errorprone.annotations.FormatString; 24 import java.util.Collection; 25 import java.util.stream.Collectors; 26 import javax.annotation.Nullable; 27 import javax.lang.model.element.Element; 28 import javax.lang.model.element.TypeElement; 29 import javax.lang.model.type.TypeKind; 30 import javax.lang.model.type.TypeMirror; 31 32 /** Static helper methods for throwing errors during code generation. */ 33 public final class ProcessorErrors { 34 /** 35 * Ensures the truth of an expression involving the state of the calling instance, but not 36 * involving any parameters to the calling method. 37 * 38 * @param expression a boolean expression 39 * @param badElement the element that was at fault 40 * @param errorMessage the exception message to use if the check fails; will be converted to a 41 * string using {@link String#valueOf(Object)} 42 * @throws BadInputException if {@code expression} is false 43 */ checkState( boolean expression, Element badElement, @Nullable Object errorMessage)44 public static void checkState( 45 boolean expression, 46 Element badElement, 47 @Nullable Object errorMessage) { 48 Preconditions.checkNotNull(badElement); 49 if (!expression) { 50 throw new BadInputException(String.valueOf(errorMessage), badElement); 51 } 52 } 53 54 /** 55 * Ensures the truth of an expression involving the state of the calling instance, but not 56 * involving any parameters to the calling method. 57 * 58 * <p>e.g. checkState(foo.isABar(), "Failed because of %s is not a bar", foo); 59 * 60 * @param expression a boolean expression 61 * @param badElement the element that was at fault 62 * @param errorMessageTemplate a template for the exception message should the check fail. The 63 * message is formed by replacing each {@code %s} placeholder in the template with an 64 * argument. These are matched by position - the first {@code %s} gets {@code 65 * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in 66 * square braces. Unmatched placeholders will be left as-is. 67 * @param errorMessageArgs the arguments to be substituted into the message template. Arguments 68 * are converted to strings using {@link String#valueOf(Object)}. 69 * @throws BadInputException if {@code expression} is false 70 * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or 71 * {@code errorMessageArgs} is null (don't let this happen) 72 */ 73 @FormatMethod checkState( boolean expression, Element badElement, @Nullable @FormatString String errorMessageTemplate, @Nullable Object... errorMessageArgs)74 public static void checkState( 75 boolean expression, 76 Element badElement, 77 @Nullable @FormatString String errorMessageTemplate, 78 @Nullable Object... errorMessageArgs) { 79 Preconditions.checkNotNull(badElement); 80 if (!expression) { 81 throw new BadInputException( 82 String.format(errorMessageTemplate, errorMessageArgs), badElement); 83 } 84 } 85 86 /** 87 * Ensures the truth of an expression involving the state of the calling instance, but not 88 * involving any parameters to the calling method. 89 * 90 * @param expression a boolean expression 91 * @param badElements the element that were at fault 92 * @param errorMessage the exception message to use if the check fails; will be converted to a 93 * string using {@link String#valueOf(Object)} 94 * @throws BadInputException if {@code expression} is false 95 */ checkState( boolean expression, Collection<? extends Element> badElements, @Nullable Object errorMessage)96 public static void checkState( 97 boolean expression, 98 Collection<? extends Element> badElements, 99 @Nullable Object errorMessage) { 100 Preconditions.checkNotNull(badElements); 101 if (!expression) { 102 Preconditions.checkState(!badElements.isEmpty()); 103 throw new BadInputException(String.valueOf(errorMessage), badElements); 104 } 105 } 106 107 /** 108 * Ensures the truth of an expression involving the state of the calling instance, but not 109 * involving any parameters to the calling method. 110 * 111 * @param expression a boolean expression 112 * @param badElements the elements that were at fault 113 * @param errorMessageTemplate a template for the exception message should the check fail. The 114 * message is formed by replacing each {@code %s} placeholder in the template with an 115 * argument. These are matched by position - the first {@code %s} gets {@code 116 * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in 117 * square braces. Unmatched placeholders will be left as-is. 118 * @param errorMessageArgs the arguments to be substituted into the message template. Arguments 119 * are converted to strings using {@link String#valueOf(Object)}. 120 * @throws BadInputException if {@code expression} is false 121 * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or 122 * {@code errorMessageArgs} is null (don't let this happen) 123 */ 124 @FormatMethod checkState( boolean expression, Collection<? extends Element> badElements, @Nullable @FormatString String errorMessageTemplate, @Nullable Object... errorMessageArgs)125 public static void checkState( 126 boolean expression, 127 Collection<? extends Element> badElements, 128 @Nullable @FormatString String errorMessageTemplate, 129 @Nullable Object... errorMessageArgs) { 130 Preconditions.checkNotNull(badElements); 131 if (!expression) { 132 Preconditions.checkState(!badElements.isEmpty()); 133 throw new BadInputException( 134 String.format(errorMessageTemplate, errorMessageArgs), badElements); 135 } 136 } 137 138 /** 139 * Ensures that the given element is not an error kind and does not inherit from an error kind. 140 * 141 * @param element the element to check 142 * @throws ErrorTypeException if {@code element} inherits from an error kind. 143 */ checkNotErrorKind(TypeElement element)144 public static void checkNotErrorKind(TypeElement element) { 145 TypeMirror currType = element.asType(); 146 ImmutableList.Builder<String> typeHierarchy = ImmutableList.builder(); 147 while (currType.getKind() != TypeKind.NONE) { 148 typeHierarchy.add(currType.toString()); 149 if (currType.getKind() == TypeKind.ERROR) { 150 throw new ErrorTypeException( 151 String.format( 152 "%s, type hierarchy contains error kind, %s." 153 + "\n\tThe partially resolved hierarchy is:\n\t\t%s", 154 element, 155 currType, 156 typeHierarchy.build().stream().collect(Collectors.joining(" -> "))), 157 element); 158 } 159 currType = MoreTypes.asTypeElement(currType).getSuperclass(); 160 } 161 } 162 ProcessorErrors()163 private ProcessorErrors() {} 164 } 165