• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.base;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
20 import static com.google.auto.common.MoreElements.asType;
21 import static com.google.auto.common.MoreElements.isType;
22 import static com.google.auto.common.MoreTypes.asDeclared;
23 
24 import androidx.room.compiler.processing.XAnnotation;
25 import androidx.room.compiler.processing.XElement;
26 import androidx.room.compiler.processing.XExecutableElement;
27 import androidx.room.compiler.processing.XProcessingEnv;
28 import androidx.room.compiler.processing.XType;
29 import androidx.room.compiler.processing.XTypeElement;
30 import com.google.auto.common.AnnotationMirrors;
31 import com.google.auto.common.MoreTypes;
32 import com.google.common.base.Ascii;
33 import com.google.common.collect.ImmutableList;
34 import com.squareup.javapoet.ClassName;
35 import dagger.Reusable;
36 import dagger.internal.codegen.compileroption.CompilerOptions;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Optional;
41 import javax.inject.Inject;
42 import javax.lang.model.element.AnnotationMirror;
43 import javax.lang.model.element.AnnotationValue;
44 import javax.lang.model.element.AnnotationValueVisitor;
45 import javax.lang.model.element.Element;
46 import javax.lang.model.element.ElementKind;
47 import javax.lang.model.element.ElementVisitor;
48 import javax.lang.model.element.ExecutableElement;
49 import javax.lang.model.element.PackageElement;
50 import javax.lang.model.element.TypeElement;
51 import javax.lang.model.element.TypeParameterElement;
52 import javax.lang.model.element.VariableElement;
53 import javax.lang.model.type.ArrayType;
54 import javax.lang.model.type.DeclaredType;
55 import javax.lang.model.type.ErrorType;
56 import javax.lang.model.type.ExecutableType;
57 import javax.lang.model.type.TypeKind;
58 import javax.lang.model.type.TypeMirror;
59 import javax.lang.model.type.TypeVisitor;
60 import javax.lang.model.type.WildcardType;
61 import javax.lang.model.util.AbstractElementVisitor8;
62 import javax.lang.model.util.SimpleAnnotationValueVisitor8;
63 import javax.lang.model.util.SimpleTypeVisitor8;
64 
65 /**
66  * A fork of {@link com.google.auto.common.SuperficialValidation}.
67  *
68  * <p>This fork makes a couple changes from the original:
69  *
70  * <ul>
71  *   <li>Throws {@link ValidationException} rather than returning {@code false} for invalid types.
72  *   <li>Fixes a bug that incorrectly validates error types in annotations (b/213880825)
73  *   <li>Exposes extra methods needed to validate various parts of an element rather than just the
74  *       entire element.
75  * </ul>
76  */
77 @Reusable
78 public final class DaggerSuperficialValidation {
79 
80   /**
81    * Returns the type element with the given class name or throws {@link ValidationException} if it
82    * is not accessible in the current compilation.
83    */
requireTypeElement(XProcessingEnv processingEnv, ClassName className)84   public static XTypeElement requireTypeElement(XProcessingEnv processingEnv, ClassName className) {
85     return requireTypeElement(processingEnv, className.canonicalName());
86   }
87 
88   /**
89    * Returns the type element with the given class name or throws {@link ValidationException} if it
90    * is not accessible in the current compilation.
91    */
requireTypeElement(XProcessingEnv processingEnv, String className)92   public static XTypeElement requireTypeElement(XProcessingEnv processingEnv, String className) {
93     XTypeElement type = processingEnv.findTypeElement(className);
94     if (type == null) {
95       throw new ValidationException.KnownErrorType(className);
96     }
97     return type;
98   }
99 
100   private final boolean isStrictValidationEnabled;
101 
102   @Inject
DaggerSuperficialValidation(CompilerOptions compilerOptions)103   DaggerSuperficialValidation(CompilerOptions compilerOptions) {
104     this(compilerOptions.strictSuperficialValidation());
105   }
106 
DaggerSuperficialValidation(boolean isStrictValidationEnabled)107   private DaggerSuperficialValidation(boolean isStrictValidationEnabled) {
108     this.isStrictValidationEnabled = isStrictValidationEnabled;
109   }
110 
111   /**
112    * Validates the {@link XElement#getType()} type of the given element.
113    *
114    * <p>Validating the type also validates any types it references, such as any type arguments or
115    * type bounds. For an {@link ExecutableType}, the parameter and return types must be fully
116    * defined, as must types declared in a {@code throws} clause or in the bounds of any type
117    * parameters.
118    */
validateTypeOf(XElement element)119   public void validateTypeOf(XElement element) {
120     validateTypeOf(toJavac(element));
121   }
122 
validateTypeOf(Element element)123   private void validateTypeOf(Element element) {
124     try {
125       validateType(Ascii.toLowerCase(element.getKind().name()), element.asType());
126     } catch (RuntimeException exception) {
127       throw ValidationException.from(exception).append(element);
128     }
129   }
130 
131   /**
132    * Validates the {@link XElement#getSuperType()} type of the given element.
133    *
134    * <p>Validating the type also validates any types it references, such as any type arguments or
135    * type bounds.
136    */
validateSuperTypeOf(XTypeElement element)137   public void validateSuperTypeOf(XTypeElement element) {
138     validateSuperTypeOf(toJavac(element));
139   }
140 
validateSuperTypeOf(TypeElement element)141   private void validateSuperTypeOf(TypeElement element) {
142     try {
143       validateType("superclass", element.getSuperclass());
144     } catch (RuntimeException exception) {
145       throw ValidationException.from(exception).append(element);
146     }
147   }
148 
149   /**
150    * Validates the {@link XExecutableElement#getThrownTypes()} types of the given element.
151    *
152    * <p>Validating the type also validates any types it references, such as any type arguments or
153    * type bounds.
154    */
validateThrownTypesOf(XExecutableElement element)155   public void validateThrownTypesOf(XExecutableElement element) {
156     validateThrownTypesOf(toJavac(element));
157   }
158 
validateThrownTypesOf(ExecutableElement element)159   private void validateThrownTypesOf(ExecutableElement element) {
160     try {
161       validateTypes("thrown type", element.getThrownTypes());
162     } catch (RuntimeException exception) {
163       throw ValidationException.from(exception).append(element);
164     }
165   }
166 
167   /**
168    * Validates the annotation types of the given element.
169    *
170    * <p>Note: this method does not validate annotation values. This method is useful if you care
171    * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
172    * such cases, we just need to validate the annotation's type.
173    */
validateAnnotationTypesOf(XElement element)174   public void validateAnnotationTypesOf(XElement element) {
175     validateAnnotationTypesOf(toJavac(element));
176   }
177 
178   /**
179    * Validates the annotation types of the given element.
180    *
181    * <p>Note: this method does not validate annotation values. This method is useful if you care
182    * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
183    * such cases, we just need to validate the annotation's type.
184    */
validateAnnotationTypesOf(Element element)185   public void validateAnnotationTypesOf(Element element) {
186     element
187         .getAnnotationMirrors()
188         .forEach(annotation -> validateAnnotationTypeOf(element, annotation));
189   }
190 
191   /**
192    * Validates the type of the given annotation.
193    *
194    * <p>The annotation is assumed to be annotating the given element, but this is not checked. The
195    * element is only in the error message if a {@link ValidatationException} is thrown.
196    *
197    * <p>Note: this method does not validate annotation values. This method is useful if you care
198    * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
199    * such cases, we just need to validate the annotation's type.
200    */
201   // TODO(bcorso): See CL/427767370 for suggestions to make this API clearer.
validateAnnotationTypeOf(XElement element, XAnnotation annotation)202   public void validateAnnotationTypeOf(XElement element, XAnnotation annotation) {
203     validateAnnotationTypeOf(toJavac(element), toJavac(annotation));
204   }
205 
206   /**
207    * Validates the type of the given annotation.
208    *
209    * <p>The annotation is assumed to be annotating the given element, but this is not checked. The
210    * element is only in the error message if a {@link ValidatationException} is thrown.
211    *
212    * <p>Note: this method does not validate annotation values. This method is useful if you care
213    * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
214    * such cases, we just need to validate the annotation's type.
215    */
validateAnnotationTypeOf(Element element, AnnotationMirror annotation)216   public void validateAnnotationTypeOf(Element element, AnnotationMirror annotation) {
217     try {
218       validateType("annotation type", annotation.getAnnotationType());
219     } catch (RuntimeException exception) {
220       throw ValidationException.from(exception).append(annotation).append(element);
221     }
222   }
223 
224   /** Validate the annotations of the given element. */
validateAnnotationsOf(XElement element)225   public void validateAnnotationsOf(XElement element) {
226     validateAnnotationsOf(toJavac(element));
227   }
228 
validateAnnotationsOf(Element element)229   public void validateAnnotationsOf(Element element) {
230     try {
231       validateAnnotations(element.getAnnotationMirrors());
232     } catch (RuntimeException exception) {
233       throw ValidationException.from(exception).append(element);
234     }
235   }
236 
validateAnnotationOf(XElement element, XAnnotation annotation)237   public void validateAnnotationOf(XElement element, XAnnotation annotation) {
238     validateAnnotationOf(toJavac(element), toJavac(annotation));
239   }
240 
validateAnnotationOf(Element element, AnnotationMirror annotation)241   public void validateAnnotationOf(Element element, AnnotationMirror annotation) {
242     try {
243       validateAnnotation(annotation);
244     } catch (RuntimeException exception) {
245       throw ValidationException.from(exception).append(element);
246     }
247   }
248 
249   /**
250    * Validate the type hierarchy for the given type (with the given type description) within the
251    * given element.
252    *
253    * <p>Validation includes all superclasses, interfaces, and type parameters of those types.
254    */
validateTypeHierarchyOf(String typeDescription, XElement element, XType type)255   public void validateTypeHierarchyOf(String typeDescription, XElement element, XType type) {
256     try {
257       validateTypeHierarchy(typeDescription, type);
258     } catch (RuntimeException exception) {
259       throw ValidationException.from(exception).append(toJavac(element));
260     }
261   }
262 
validateTypeHierarchy(String desc, XType type)263   private void validateTypeHierarchy(String desc, XType type) {
264     validateType(desc, toJavac(type));
265     try {
266       type.getSuperTypes().forEach(supertype -> validateTypeHierarchy("supertype", supertype));
267     } catch (RuntimeException exception) {
268       throw ValidationException.from(exception).append(desc, toJavac(type));
269     }
270   }
271 
272   /**
273    * Returns true if all of the given elements return true from {@link #validateElement(Element)}.
274    */
validateElements(Iterable<? extends Element> elements)275   public void validateElements(Iterable<? extends Element> elements) {
276     for (Element element : elements) {
277       validateElement(element);
278     }
279   }
280 
281   private final ElementVisitor<Void, Void> elementValidatingVisitor =
282       new AbstractElementVisitor8<Void, Void>() {
283         @Override
284         public Void visitPackage(PackageElement e, Void p) {
285           // don't validate enclosed elements because it will return types in the package
286           validateAnnotations(e.getAnnotationMirrors());
287           return null;
288         }
289 
290         @Override
291         public Void visitType(TypeElement e, Void p) {
292           validateBaseElement(e);
293           validateElements(e.getTypeParameters());
294           validateTypes("interface", e.getInterfaces());
295           validateType("superclass", e.getSuperclass());
296           return null;
297         }
298 
299         @Override
300         public Void visitVariable(VariableElement e, Void p) {
301           validateBaseElement(e);
302           return null;
303         }
304 
305         @Override
306         public Void visitExecutable(ExecutableElement e, Void p) {
307           AnnotationValue defaultValue = e.getDefaultValue();
308           validateBaseElement(e);
309           if (defaultValue != null) {
310             validateAnnotationValue(defaultValue, e.getReturnType());
311           }
312           validateType("return type", e.getReturnType());
313           validateTypes("thrown type", e.getThrownTypes());
314           validateElements(e.getTypeParameters());
315           validateElements(e.getParameters());
316           return null;
317         }
318 
319         @Override
320         public Void visitTypeParameter(TypeParameterElement e, Void p) {
321           validateBaseElement(e);
322           validateTypes("bound type", e.getBounds());
323           return null;
324         }
325 
326         @Override
327         public Void visitUnknown(Element e, Void p) {
328           // just assume that unknown elements are OK
329           return null;
330         }
331       };
332 
333   /**
334    * Returns true if all types referenced by the given element are defined. The exact meaning of
335    * this depends on the kind of element. For packages, it means that all annotations on the package
336    * are fully defined. For other element kinds, it means that types referenced by the element,
337    * anything it contains, and any of its annotations element are all defined.
338    */
validateElement(XElement element)339   public void validateElement(XElement element) {
340     validateElement(toJavac(element));
341   }
342 
343   /**
344    * Returns true if all types referenced by the given element are defined. The exact meaning of
345    * this depends on the kind of element. For packages, it means that all annotations on the package
346    * are fully defined. For other element kinds, it means that types referenced by the element,
347    * anything it contains, and any of its annotations element are all defined.
348    */
validateElement(Element element)349   public void validateElement(Element element) {
350     try {
351       element.accept(elementValidatingVisitor, null);
352     } catch (RuntimeException exception) {
353       throw ValidationException.from(exception).append(element);
354     }
355   }
356 
validateBaseElement(Element e)357   private void validateBaseElement(Element e) {
358     validateType(Ascii.toLowerCase(e.getKind().name()), e.asType());
359     validateAnnotations(e.getAnnotationMirrors());
360     validateElements(e.getEnclosedElements());
361   }
362 
validateTypes(String desc, Iterable<? extends TypeMirror> types)363   private void validateTypes(String desc, Iterable<? extends TypeMirror> types) {
364     for (TypeMirror type : types) {
365       validateType(desc, type);
366     }
367   }
368 
369   /*
370    * This visitor does not test type variables specifically, but it seems that that is not actually
371    * an issue.  Javac turns the whole type parameter into an error type if it can't figure out the
372    * bounds.
373    */
374   private final TypeVisitor<Void, Void> typeValidatingVisitor =
375       new SimpleTypeVisitor8<Void, Void>() {
376         @Override
377         protected Void defaultAction(TypeMirror t, Void p) {
378           return null;
379         }
380 
381         @Override
382         public Void visitArray(ArrayType t, Void p) {
383           validateType("array component type", t.getComponentType());
384           return null;
385         }
386 
387         @Override
388         public Void visitDeclared(DeclaredType t, Void p) {
389           if (isStrictValidationEnabled) {
390             // There's a bug in TypeVisitor which will visit the visitDeclared() method rather than
391             // visitError() even when it's an ERROR kind. Thus, we check the kind directly here and
392             // fail validation if it's an ERROR kind (see b/213880825).
393             if (t.getKind() == TypeKind.ERROR) {
394               throw new ValidationException.KnownErrorType(t);
395             }
396           }
397           validateTypes("type argument", t.getTypeArguments());
398           return null;
399         }
400 
401         @Override
402         public Void visitError(ErrorType t, Void p) {
403           throw new ValidationException.KnownErrorType(t);
404         }
405 
406         @Override
407         public Void visitUnknown(TypeMirror t, Void p) {
408           // just make the default choice for unknown types
409           return defaultAction(t, p);
410         }
411 
412         @Override
413         public Void visitWildcard(WildcardType t, Void p) {
414           TypeMirror extendsBound = t.getExtendsBound();
415           TypeMirror superBound = t.getSuperBound();
416           if (extendsBound != null) {
417             validateType("extends bound type", extendsBound);
418           }
419           if (superBound != null) {
420             validateType("super bound type", superBound);
421           }
422           return null;
423         }
424 
425         @Override
426         public Void visitExecutable(ExecutableType t, Void p) {
427           validateTypes("parameter type", t.getParameterTypes());
428           validateType("return type", t.getReturnType());
429           validateTypes("thrown type", t.getThrownTypes());
430           validateTypes("type variable", t.getTypeVariables());
431           return null;
432         }
433       };
434 
435   /**
436    * Returns true if the given type is fully defined. This means that the type itself is defined, as
437    * are any types it references, such as any type arguments or type bounds. For an {@link
438    * ExecutableType}, the parameter and return types must be fully defined, as must types declared
439    * in a {@code throws} clause or in the bounds of any type parameters.
440    */
validateType(String desc, TypeMirror type)441   private void validateType(String desc, TypeMirror type) {
442     try {
443       type.accept(typeValidatingVisitor, null);
444       if (isStrictValidationEnabled) {
445         // Note: We don't actually expect to get an ERROR type here as it should have been caught
446         // by the visitError() or visitDeclared()  methods above. However, we check here as a last
447         // resort.
448         if (type.getKind() == TypeKind.ERROR) {
449           // In this case, the type is not guaranteed to be a DeclaredType, so we report the
450           // toString() of the type. We could report using UnknownErrorType but the type's toString
451           // may actually contain useful information.
452           throw new ValidationException.KnownErrorType(type.toString());
453         }
454       }
455     } catch (RuntimeException e) {
456       throw ValidationException.from(e).append(desc, type);
457     }
458   }
459 
validateAnnotations(Iterable<? extends AnnotationMirror> annotationMirrors)460   private void validateAnnotations(Iterable<? extends AnnotationMirror> annotationMirrors) {
461     for (AnnotationMirror annotationMirror : annotationMirrors) {
462       validateAnnotation(annotationMirror);
463     }
464   }
465 
validateAnnotation(AnnotationMirror annotationMirror)466   private void validateAnnotation(AnnotationMirror annotationMirror) {
467     try {
468       validateType("annotation type", annotationMirror.getAnnotationType());
469       validateAnnotationValues(annotationMirror.getElementValues());
470     } catch (RuntimeException exception) {
471       throw ValidationException.from(exception).append(annotationMirror);
472     }
473   }
474 
validateAnnotationValues( Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap)475   private void validateAnnotationValues(
476       Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) {
477     valueMap.forEach(
478         (method, annotationValue) -> {
479           try {
480             TypeMirror expectedType = method.getReturnType();
481             validateAnnotationValue(annotationValue, expectedType);
482           } catch (RuntimeException exception) {
483             throw ValidationException.from(exception)
484                 .append(String.format("annotation method: %s %s", method.getReturnType(), method));
485           }
486         });
487   }
488 
validateAnnotationValue(AnnotationValue annotationValue, TypeMirror expectedType)489   private void validateAnnotationValue(AnnotationValue annotationValue, TypeMirror expectedType) {
490     annotationValue.accept(valueValidatingVisitor, expectedType);
491   }
492 
493   private final AnnotationValueVisitor<Void, TypeMirror> valueValidatingVisitor =
494       new SimpleAnnotationValueVisitor8<Void, TypeMirror>() {
495         @Override
496         protected Void defaultAction(Object o, TypeMirror expectedType) {
497           try {
498             validateIsTypeOf(o.getClass(), expectedType);
499           } catch (RuntimeException exception) {
500             throw ValidationException.from(exception)
501                 .append(exceptionMessage("DEFAULT", o, expectedType));
502           }
503           return null;
504         }
505 
506         @Override
507         public Void visitString(String str, TypeMirror expectedType) {
508           try {
509             if (!MoreTypes.isTypeOf(String.class, expectedType)) {
510               if (str.contentEquals("<error>")) {
511                 // Invalid annotation value types will visit visitString() with a value of "<error>"
512                 // Technically, we don't know the error type in this case, but it will be referred
513                 // to as "<error>" in the dependency trace, so we use that.
514                 throw new ValidationException.KnownErrorType("<error>");
515               } else {
516                 throw new ValidationException.UnknownErrorType();
517               }
518             }
519           } catch (RuntimeException exception) {
520             throw ValidationException.from(exception)
521                 .append(exceptionMessage("STRING", str, expectedType));
522           }
523           return null;
524         }
525 
526         @Override
527         public Void visitUnknown(AnnotationValue av, TypeMirror expectedType) {
528           // just take the default action for the unknown
529           defaultAction(av, expectedType);
530           return null;
531         }
532 
533         @Override
534         public Void visitAnnotation(AnnotationMirror a, TypeMirror expectedType) {
535           try {
536             validateIsEquivalentType(a.getAnnotationType(), expectedType);
537             validateAnnotation(a);
538           } catch (RuntimeException exception) {
539             throw ValidationException.from(exception)
540                 .append(exceptionMessage("ANNOTATION", a, expectedType));
541           }
542           return null;
543         }
544 
545         @Override
546         public Void visitArray(List<? extends AnnotationValue> values, TypeMirror expectedType) {
547           try {
548             if (!expectedType.getKind().equals(TypeKind.ARRAY)) {
549               throw new ValidationException.UnknownErrorType();
550             }
551             TypeMirror componentType = MoreTypes.asArray(expectedType).getComponentType();
552             for (AnnotationValue value : values) {
553               value.accept(this, componentType);
554             }
555           } catch (RuntimeException exception) {
556             throw ValidationException.from(exception)
557                 .append(exceptionMessage("ARRAY", values, expectedType));
558           }
559           return null;
560         }
561 
562         @Override
563         public Void visitEnumConstant(VariableElement enumConstant, TypeMirror expectedType) {
564           try {
565             validateIsEquivalentType(asDeclared(enumConstant.asType()), expectedType);
566             validateElement(enumConstant);
567           } catch (RuntimeException exception) {
568             throw ValidationException.from(exception)
569                 .append(exceptionMessage("ENUM_CONSTANT", enumConstant, expectedType));
570           }
571           return null;
572         }
573 
574         @Override
575         public Void visitType(TypeMirror type, TypeMirror expectedType) {
576           try {
577             // We could check assignability here, but would require a Types instance. Since this
578             // isn't really the sort of thing that shows up in a bad AST from upstream compilation
579             // we ignore the expected type and just validate the type.  It might be wrong, but
580             // it's valid.
581             validateType("annotation value type", type);
582           } catch (RuntimeException exception) {
583             throw ValidationException.from(exception)
584                 .append(exceptionMessage("TYPE", type, expectedType));
585           }
586           return null;
587         }
588 
589         @Override
590         public Void visitBoolean(boolean b, TypeMirror expectedType) {
591           try {
592             validateIsTypeOf(Boolean.TYPE, expectedType);
593           } catch (RuntimeException exception) {
594             throw ValidationException.from(exception)
595                 .append(exceptionMessage("BOOLEAN", b, expectedType));
596           }
597           return null;
598         }
599 
600         @Override
601         public Void visitByte(byte b, TypeMirror expectedType) {
602           try {
603             validateIsTypeOf(Byte.TYPE, expectedType);
604           } catch (RuntimeException exception) {
605             throw ValidationException.from(exception)
606                 .append(exceptionMessage("BYTE", b, expectedType));
607           }
608           return null;
609         }
610 
611         @Override
612         public Void visitChar(char c, TypeMirror expectedType) {
613           try {
614             validateIsTypeOf(Character.TYPE, expectedType);
615           } catch (RuntimeException exception) {
616             throw ValidationException.from(exception)
617                 .append(exceptionMessage("CHAR", c, expectedType));
618           }
619           return null;
620         }
621 
622         @Override
623         public Void visitDouble(double d, TypeMirror expectedType) {
624           try {
625             validateIsTypeOf(Double.TYPE, expectedType);
626           } catch (RuntimeException exception) {
627             throw ValidationException.from(exception)
628                 .append(exceptionMessage("DOUBLE", d, expectedType));
629           }
630           return null;
631         }
632 
633         @Override
634         public Void visitFloat(float f, TypeMirror expectedType) {
635           try {
636             validateIsTypeOf(Float.TYPE, expectedType);
637           } catch (RuntimeException exception) {
638             throw ValidationException.from(exception)
639                 .append(exceptionMessage("FLOAT", f, expectedType));
640           }
641           return null;
642         }
643 
644         @Override
645         public Void visitInt(int i, TypeMirror expectedType) {
646           try {
647             validateIsTypeOf(Integer.TYPE, expectedType);
648           } catch (RuntimeException exception) {
649             throw ValidationException.from(exception)
650                 .append(exceptionMessage("INT", i, expectedType));
651           }
652           return null;
653         }
654 
655         @Override
656         public Void visitLong(long l, TypeMirror expectedType) {
657           try {
658             validateIsTypeOf(Long.TYPE, expectedType);
659           } catch (RuntimeException exception) {
660             throw ValidationException.from(exception)
661                 .append(exceptionMessage("LONG", l, expectedType));
662           }
663           return null;
664         }
665 
666         @Override
667         public Void visitShort(short s, TypeMirror expectedType) {
668           try {
669             validateIsTypeOf(Short.TYPE, expectedType);
670           } catch (RuntimeException exception) {
671             throw ValidationException.from(exception)
672                 .append(exceptionMessage("SHORT", s, expectedType));
673           }
674           return null;
675         }
676 
677         private <T> String exceptionMessage(String valueType, T value, TypeMirror expectedType) {
678           return String.format(
679               "annotation value (%s): value '%s' with expected type %s",
680               valueType, value, expectedType);
681         }
682       };
683 
validateIsTypeOf(Class<?> clazz, TypeMirror expectedType)684   private void validateIsTypeOf(Class<?> clazz, TypeMirror expectedType) {
685     if (!MoreTypes.isTypeOf(clazz, expectedType)) {
686       throw new ValidationException.UnknownErrorType();
687     }
688   }
689 
validateIsEquivalentType(DeclaredType type, TypeMirror expectedType)690   private void validateIsEquivalentType(DeclaredType type, TypeMirror expectedType) {
691     if (!MoreTypes.equivalence().equivalent(type, expectedType)) {
692       throw new ValidationException.KnownErrorType(type);
693     }
694   }
695 
696   /**
697    * A runtime exception that can be used during superficial validation to collect information about
698    * unexpected exceptions during validation.
699    */
700   public abstract static class ValidationException extends RuntimeException {
701     /** A {@link ValidationException} that originated from an unexpected exception. */
702     public static final class UnexpectedException extends ValidationException {
UnexpectedException(Throwable throwable)703       private UnexpectedException(Throwable throwable) {
704         super(throwable);
705       }
706     }
707 
708     /** A {@link ValidationException} that originated from a known error type. */
709     public static final class KnownErrorType extends ValidationException {
710       private final String errorTypeName;
711 
KnownErrorType(DeclaredType errorType)712       private KnownErrorType(DeclaredType errorType) {
713         Element errorElement = errorType.asElement();
714         this.errorTypeName =
715             isType(errorElement)
716                 ? asType(errorElement).getQualifiedName().toString()
717                 // Maybe this case should be handled by UnknownErrorType?
718                 : errorElement.getSimpleName().toString();
719       }
720 
KnownErrorType(String errorTypeName)721       private KnownErrorType(String errorTypeName) {
722         this.errorTypeName = errorTypeName;
723       }
724 
getErrorTypeName()725       public String getErrorTypeName() {
726         return errorTypeName;
727       }
728     }
729 
730     /** A {@link ValidationException} that originated from an unknown error type. */
731     public static final class UnknownErrorType extends ValidationException {}
732 
from(Throwable throwable)733     private static ValidationException from(Throwable throwable) {
734       // We only ever create one instance of the ValidationException.
735       return throwable instanceof ValidationException
736           ? ((ValidationException) throwable)
737           : new UnexpectedException(throwable);
738     }
739 
740     private Optional<Element> lastReportedElement = Optional.empty();
741     private final List<String> messages = new ArrayList<>();
742 
ValidationException()743     private ValidationException() {
744       super("");
745     }
746 
ValidationException(Throwable throwable)747     private ValidationException(Throwable throwable) {
748       super("", throwable);
749     }
750 
751     /**
752      * Appends a message for the given element and returns this instance of {@link
753      * ValidationException}
754      */
append(Element element)755     private ValidationException append(Element element) {
756       lastReportedElement = Optional.of(element);
757       return append(getMessageForElement(element));
758     }
759 
760     /**
761      * Appends a message for the given type mirror and returns this instance of {@link
762      * ValidationException}
763      */
append(String desc, TypeMirror type)764     private ValidationException append(String desc, TypeMirror type) {
765       return append(String.format("type (%s %s): %s", type.getKind().name(), desc, type));
766     }
767 
768     /**
769      * Appends a message for the given annotation mirror and returns this instance of {@link
770      * ValidationException}
771      */
append(AnnotationMirror annotationMirror)772     private ValidationException append(AnnotationMirror annotationMirror) {
773       // Note: Calling #toString() directly on the annotation throws NPE (b/216180336).
774       return append(String.format("annotation: %s", AnnotationMirrors.toString(annotationMirror)));
775     }
776 
777     /** Appends the given message and returns this instance of {@link ValidationException} */
append(String message)778     private ValidationException append(String message) {
779       messages.add(message);
780       return this;
781     }
782 
783     @Override
getMessage()784     public String getMessage() {
785       return String.format("\n  Validation trace:\n    => %s", getTrace());
786     }
787 
getTrace()788     public String getTrace() {
789       return String.join("\n    => ", getMessageInternal().reverse());
790     }
791 
getMessageInternal()792     private ImmutableList<String> getMessageInternal() {
793       if (!lastReportedElement.isPresent()) {
794         return ImmutableList.copyOf(messages);
795       }
796       // Append any enclosing element information if needed.
797       List<String> newMessages = new ArrayList<>(messages);
798       Element element = lastReportedElement.get();
799       while (shouldAppendEnclosingElement(element)) {
800         element = element.getEnclosingElement();
801         newMessages.add(getMessageForElement(element));
802       }
803       return ImmutableList.copyOf(newMessages);
804     }
805 
shouldAppendEnclosingElement(Element element)806     private static boolean shouldAppendEnclosingElement(Element element) {
807       return element.getEnclosingElement() != null
808           // We don't report enclosing elements for types because the type name should contain any
809           // enclosing type and package information we need.
810           && !isType(element)
811           && (isExecutable(element.getEnclosingElement()) || isType(element.getEnclosingElement()));
812     }
813 
isExecutable(Element element)814     private static boolean isExecutable(Element element) {
815       return element.getKind() == ElementKind.METHOD
816           || element.getKind() == ElementKind.CONSTRUCTOR;
817     }
818 
getMessageForElement(Element element)819     private String getMessageForElement(Element element) {
820       return String.format("element (%s): %s", element.getKind().name(), element);
821     }
822   }
823 }
824