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