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