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