1 /* 2 * Copyright (C) 2014 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 dagger.internal.codegen.DaggerStreams.toImmutableSet; 20 import static dagger.internal.codegen.ElementFormatter.elementToString; 21 import static javax.tools.Diagnostic.Kind.ERROR; 22 import static javax.tools.Diagnostic.Kind.NOTE; 23 import static javax.tools.Diagnostic.Kind.WARNING; 24 25 import com.google.auto.value.AutoValue; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.common.graph.Traverser; 28 import com.google.errorprone.annotations.CanIgnoreReturnValue; 29 import com.google.errorprone.annotations.CheckReturnValue; 30 import java.util.Optional; 31 import javax.annotation.processing.Messager; 32 import javax.lang.model.element.AnnotationMirror; 33 import javax.lang.model.element.AnnotationValue; 34 import javax.lang.model.element.Element; 35 import javax.tools.Diagnostic; 36 import javax.tools.Diagnostic.Kind; 37 38 /** A collection of issues to report for source code. */ 39 @AutoValue 40 abstract class ValidationReport<T extends Element> { 41 42 /** 43 * The subject of the report. Should be an element within a compilation unit being processed by 44 * this compilation task. 45 */ subject()46 abstract T subject(); 47 48 /** The items to report for the {@linkplain #subject() subject}. */ items()49 abstract ImmutableSet<Item> items(); 50 51 /** Returns the {@link #items()} from this report and all transitive subreports. */ allItems()52 ImmutableSet<Item> allItems() { 53 return allReports() 54 .stream() 55 .flatMap(report -> report.items().stream()) 56 .collect(toImmutableSet()); 57 } 58 59 /** Other reports associated with this one. */ subreports()60 abstract ImmutableSet<ValidationReport<?>> subreports(); 61 62 private static final Traverser<ValidationReport<?>> SUBREPORTS = 63 Traverser.forTree(ValidationReport::subreports); 64 65 /** Returns this report and all transitive subreports. */ allReports()66 ImmutableSet<ValidationReport<?>> allReports() { 67 return ImmutableSet.copyOf(SUBREPORTS.depthFirstPreOrder(this)); 68 } 69 70 /** 71 * {@code true} if {@link #isClean()} should return {@code false} even if there are no error items 72 * in this report. 73 */ markedDirty()74 abstract boolean markedDirty(); 75 76 /** 77 * Returns {@code true} if there are no errors in this report or any subreports and {@link 78 * #markedDirty()} is {@code false}. 79 */ isClean()80 boolean isClean() { 81 if (markedDirty()) { 82 return false; 83 } 84 for (Item item : items()) { 85 switch (item.kind()) { 86 case ERROR: 87 return false; 88 default: 89 break; 90 } 91 } 92 for (ValidationReport<?> subreport : subreports()) { 93 if (!subreport.isClean()) { 94 return false; 95 } 96 } 97 return true; 98 } 99 100 /** 101 * Prints all {@linkplain #items() messages} to {@code messager} (and recurs for subreports). If a 102 * message's {@linkplain Item#element() element} is contained within the report's {@linkplain 103 * #subject() subject}, associates the message with the message's element. Otherwise, since 104 * {@link Diagnostic} reporting is expected to be associated with elements that are currently 105 * being compiled, associates the message with the subject itself and prepends a reference to the 106 * item's element. 107 */ printMessagesTo(Messager messager)108 void printMessagesTo(Messager messager) { 109 for (Item item : items()) { 110 if (isEnclosedIn(subject(), item.element())) { 111 if (item.annotation().isPresent()) { 112 if (item.annotationValue().isPresent()) { 113 messager.printMessage( 114 item.kind(), 115 item.message(), 116 item.element(), 117 item.annotation().get(), 118 item.annotationValue().get()); 119 } else { 120 messager.printMessage( 121 item.kind(), item.message(), item.element(), item.annotation().get()); 122 } 123 } else { 124 messager.printMessage(item.kind(), item.message(), item.element()); 125 } 126 } else { 127 String message = String.format("[%s] %s", elementToString(item.element()), item.message()); 128 messager.printMessage(item.kind(), message, subject()); 129 } 130 } 131 for (ValidationReport<?> subreport : subreports()) { 132 subreport.printMessagesTo(messager); 133 } 134 } 135 isEnclosedIn(Element parent, Element child)136 private static boolean isEnclosedIn(Element parent, Element child) { 137 Element current = child; 138 while (current != null) { 139 if (current.equals(parent)) { 140 return true; 141 } 142 current = current.getEnclosingElement(); 143 } 144 return false; 145 } 146 147 @AutoValue 148 static abstract class Item { message()149 abstract String message(); kind()150 abstract Kind kind(); element()151 abstract Element element(); annotation()152 abstract Optional<AnnotationMirror> annotation(); annotationValue()153 abstract Optional<AnnotationValue> annotationValue(); 154 } 155 about(T subject)156 static <T extends Element> Builder<T> about(T subject) { 157 return new Builder<>(subject); 158 } 159 160 @CanIgnoreReturnValue 161 static final class Builder<T extends Element> { 162 private final T subject; 163 private final ImmutableSet.Builder<Item> items = ImmutableSet.builder(); 164 private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder(); 165 private boolean markedDirty; 166 Builder(T subject)167 private Builder(T subject) { 168 this.subject = subject; 169 } 170 171 @CheckReturnValue getSubject()172 T getSubject() { 173 return subject; 174 } 175 addItems(Iterable<Item> newItems)176 Builder<T> addItems(Iterable<Item> newItems) { 177 items.addAll(newItems); 178 return this; 179 } 180 addError(String message)181 Builder<T> addError(String message) { 182 return addError(message, subject); 183 } 184 addError(String message, Element element)185 Builder<T> addError(String message, Element element) { 186 return addItem(message, ERROR, element); 187 } 188 addError(String message, Element element, AnnotationMirror annotation)189 Builder<T> addError(String message, Element element, AnnotationMirror annotation) { 190 return addItem(message, ERROR, element, annotation); 191 } 192 addError( String message, Element element, AnnotationMirror annotation, AnnotationValue annotationValue)193 Builder<T> addError( 194 String message, 195 Element element, 196 AnnotationMirror annotation, 197 AnnotationValue annotationValue) { 198 return addItem(message, ERROR, element, annotation, annotationValue); 199 } 200 addWarning(String message)201 Builder<T> addWarning(String message) { 202 return addWarning(message, subject); 203 } 204 addWarning(String message, Element element)205 Builder<T> addWarning(String message, Element element) { 206 return addItem(message, WARNING, element); 207 } 208 addWarning(String message, Element element, AnnotationMirror annotation)209 Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) { 210 return addItem(message, WARNING, element, annotation); 211 } 212 addWarning( String message, Element element, AnnotationMirror annotation, AnnotationValue annotationValue)213 Builder<T> addWarning( 214 String message, 215 Element element, 216 AnnotationMirror annotation, 217 AnnotationValue annotationValue) { 218 return addItem(message, WARNING, element, annotation, annotationValue); 219 } 220 addNote(String message)221 Builder<T> addNote(String message) { 222 return addNote(message, subject); 223 } 224 addNote(String message, Element element)225 Builder<T> addNote(String message, Element element) { 226 return addItem(message, NOTE, element); 227 } 228 addNote(String message, Element element, AnnotationMirror annotation)229 Builder<T> addNote(String message, Element element, AnnotationMirror annotation) { 230 return addItem(message, NOTE, element, annotation); 231 } 232 addNote( String message, Element element, AnnotationMirror annotation, AnnotationValue annotationValue)233 Builder<T> addNote( 234 String message, 235 Element element, 236 AnnotationMirror annotation, 237 AnnotationValue annotationValue) { 238 return addItem(message, NOTE, element, annotation, annotationValue); 239 } 240 addItem(String message, Kind kind, Element element)241 Builder<T> addItem(String message, Kind kind, Element element) { 242 return addItem(message, kind, element, Optional.empty(), Optional.empty()); 243 } 244 addItem(String message, Kind kind, Element element, AnnotationMirror annotation)245 Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) { 246 return addItem(message, kind, element, Optional.of(annotation), Optional.empty()); 247 } 248 addItem( String message, Kind kind, Element element, AnnotationMirror annotation, AnnotationValue annotationValue)249 Builder<T> addItem( 250 String message, 251 Kind kind, 252 Element element, 253 AnnotationMirror annotation, 254 AnnotationValue annotationValue) { 255 return addItem(message, kind, element, Optional.of(annotation), Optional.of(annotationValue)); 256 } 257 addItem( String message, Kind kind, Element element, Optional<AnnotationMirror> annotation, Optional<AnnotationValue> annotationValue)258 private Builder<T> addItem( 259 String message, 260 Kind kind, 261 Element element, 262 Optional<AnnotationMirror> annotation, 263 Optional<AnnotationValue> annotationValue) { 264 items.add( 265 new AutoValue_ValidationReport_Item(message, kind, element, annotation, annotationValue)); 266 return this; 267 } 268 269 /** 270 * If called, then {@link #isClean()} will return {@code false} even if there are no error items 271 * in the report. 272 */ markDirty()273 void markDirty() { 274 this.markedDirty = true; 275 } 276 addSubreport(ValidationReport<?> subreport)277 Builder<T> addSubreport(ValidationReport<?> subreport) { 278 subreports.add(subreport); 279 return this; 280 } 281 282 @CheckReturnValue build()283 ValidationReport<T> build() { 284 return new AutoValue_ValidationReport<>( 285 subject, items.build(), subreports.build(), markedDirty); 286 } 287 } 288 } 289