1 /* 2 * Copyright 2016 Google LLC 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 package com.google.auto.common; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static java.nio.charset.StandardCharsets.UTF_8; 20 import static javax.lang.model.util.ElementFilter.methodsIn; 21 22 import com.google.common.base.Converter; 23 import com.google.common.base.Optional; 24 import com.google.common.base.StandardSystemProperty; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.common.collect.Range; 28 import com.google.common.io.Files; 29 import com.google.common.truth.Expect; 30 import com.google.testing.compile.CompilationRule; 31 import java.io.File; 32 import java.util.AbstractCollection; 33 import java.util.AbstractList; 34 import java.util.ArrayList; 35 import java.util.Collection; 36 import java.util.List; 37 import java.util.Set; 38 import javax.annotation.processing.AbstractProcessor; 39 import javax.annotation.processing.RoundEnvironment; 40 import javax.annotation.processing.SupportedAnnotationTypes; 41 import javax.lang.model.SourceVersion; 42 import javax.lang.model.element.Element; 43 import javax.lang.model.element.ExecutableElement; 44 import javax.lang.model.element.TypeElement; 45 import javax.lang.model.element.VariableElement; 46 import javax.lang.model.type.ArrayType; 47 import javax.lang.model.type.DeclaredType; 48 import javax.lang.model.type.TypeKind; 49 import javax.lang.model.type.TypeMirror; 50 import javax.lang.model.type.TypeVariable; 51 import javax.lang.model.type.TypeVisitor; 52 import javax.lang.model.util.Elements; 53 import javax.lang.model.util.SimpleTypeVisitor6; 54 import javax.lang.model.util.Types; 55 import javax.tools.JavaCompiler; 56 import javax.tools.JavaFileObject; 57 import javax.tools.StandardJavaFileManager; 58 import javax.tools.StandardLocation; 59 import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; 60 import org.junit.Before; 61 import org.junit.Rule; 62 import org.junit.Test; 63 import org.junit.rules.TestRule; 64 import org.junit.runner.Description; 65 import org.junit.runner.RunWith; 66 import org.junit.runners.Parameterized; 67 import org.junit.runners.model.Statement; 68 69 /** 70 * Tests that the {@link Overrides} class has behaviour consistent with javac. We test this in 71 * two ways: once with {@link Overrides.ExplicitOverrides} using javac's own {@link Elements} and 72 * {@link Types}, and once with it using the version of those objects from the Eclipse compiler 73 * (ecj). 74 * 75 * @author emcmanus@google.com (Éamonn McManus) 76 */ 77 @RunWith(Parameterized.class) 78 public class OverridesTest { 79 @Parameterized.Parameters(name = "{0}") data()80 public static ImmutableList<CompilerType> data() { 81 return ImmutableList.of(CompilerType.JAVAC, CompilerType.ECJ); 82 } 83 84 @Rule public CompilationRule compilation = new CompilationRule(); 85 @Rule public EcjCompilationRule ecjCompilation = new EcjCompilationRule(); 86 @Rule public Expect expect = Expect.create(); 87 88 public enum CompilerType { 89 JAVAC { 90 @Override initUtils(OverridesTest test)91 void initUtils(OverridesTest test) { 92 test.typeUtils = test.compilation.getTypes(); 93 test.elementUtils = test.compilation.getElements(); 94 } 95 }, 96 ECJ { 97 @Override initUtils(OverridesTest test)98 void initUtils(OverridesTest test) { 99 test.typeUtils = test.ecjCompilation.types; 100 test.elementUtils = test.ecjCompilation.elements; 101 } 102 }; 103 initUtils(OverridesTest test)104 abstract void initUtils(OverridesTest test); 105 } 106 private final CompilerType compilerType; 107 108 private Types typeUtils; 109 private Elements elementUtils; 110 private Elements javacElementUtils; 111 private Overrides javacOverrides; 112 private Overrides.ExplicitOverrides explicitOverrides; 113 OverridesTest(CompilerType compilerType)114 public OverridesTest(CompilerType compilerType) { 115 this.compilerType = compilerType; 116 } 117 118 @Before initializeTestElements()119 public void initializeTestElements() { 120 javacElementUtils = compilation.getElements(); 121 javacOverrides = new Overrides.NativeOverrides(javacElementUtils); 122 compilerType.initUtils(this); 123 explicitOverrides = new Overrides.ExplicitOverrides(typeUtils); 124 } 125 126 static class TypesForInheritance { 127 interface One { m()128 void m(); m(String x)129 void m(String x); n()130 void n(); 131 } 132 133 interface Two { m()134 void m(); m(int x)135 void m(int x); 136 } 137 138 static class Parent { m()139 public void m() {} 140 } 141 142 static class ChildOfParent extends Parent {} 143 144 static class ChildOfOne implements One { m()145 @Override public void m() {} m(String x)146 @Override public void m(String x) {} n()147 @Override public void n() {} 148 } 149 150 static class ChildOfOneAndTwo implements One, Two { m()151 @Override public void m() {} m(String x)152 @Override public void m(String x) {} m(int x)153 @Override public void m(int x) {} n()154 @Override public void n() {} 155 } 156 157 static class ChildOfParentAndOne extends Parent implements One { m()158 @Override public void m() {} m(String x)159 @Override public void m(String x) {} n()160 @Override public void n() {} 161 } 162 163 static class ChildOfParentAndOneAndTwo extends Parent implements One, Two { m(String x)164 @Override public void m(String x) {} m(int x)165 @Override public void m(int x) {} n()166 @Override public void n() {} 167 } 168 169 abstract static class AbstractChildOfOne implements One {} 170 171 abstract static class AbstractChildOfOneAndTwo implements One, Two {} 172 173 abstract static class AbstractChildOfParentAndOneAndTwo extends Parent implements One, Two {} 174 } 175 176 static class MoreTypesForInheritance { 177 interface Key {} 178 179 interface BindingType {} 180 181 interface ContributionType {} 182 183 interface HasKey { key()184 Key key(); 185 } 186 187 interface HasBindingType { bindingType()188 BindingType bindingType(); 189 } 190 191 interface HasContributionType { contributionType()192 ContributionType contributionType(); 193 } 194 195 abstract static class BindingDeclaration implements HasKey { bindingElement()196 abstract Optional<Element> bindingElement(); contributingModule()197 abstract Optional<TypeElement> contributingModule(); 198 } 199 200 abstract static class MultibindingDeclaration 201 extends BindingDeclaration implements HasBindingType, HasContributionType { key()202 @Override public abstract Key key(); contributionType()203 @Override public abstract ContributionType contributionType(); bindingType()204 @Override public abstract BindingType bindingType(); 205 } 206 } 207 208 static class TypesForVisibility { 209 public abstract static class PublicGrandparent { foo()210 public abstract String foo(); 211 } 212 213 private static class PrivateParent extends PublicGrandparent { 214 @Override foo()215 public String foo() { 216 return "foo"; 217 } 218 } 219 220 static class Child extends PrivateParent {} 221 } 222 223 static class TypesForGenerics { 224 interface XCollection<E> { add(E x)225 boolean add(E x); 226 } 227 228 interface XList<E> extends XCollection<E> { add(E x)229 @Override public boolean add(E x); 230 } 231 232 static class StringList implements XList<String> { add(String x)233 @Override public boolean add(String x) { 234 return false; 235 } 236 } 237 } 238 239 @SuppressWarnings("rawtypes") 240 static class TypesForRaw { 241 static class RawParent { frob(List x)242 void frob(List x) {} 243 } 244 245 static class RawChildOfRaw extends RawParent { frob(List x)246 @Override void frob(List x) {} 247 } 248 249 static class NonRawParent { frob(List<String> x)250 void frob(List<String> x) {} 251 } 252 253 static class RawChildOfNonRaw extends NonRawParent { frob(List x)254 @Override void frob(List x) {} 255 } 256 } 257 258 @Test overridesInheritance()259 public void overridesInheritance() { 260 checkOverridesInContainedClasses(TypesForInheritance.class); 261 } 262 263 @Test overridesMoreInheritance()264 public void overridesMoreInheritance() { 265 checkOverridesInContainedClasses(MoreTypesForInheritance.class); 266 } 267 268 @Test overridesVisibility()269 public void overridesVisibility() { 270 checkOverridesInContainedClasses(TypesForVisibility.class); 271 } 272 273 @Test overridesGenerics()274 public void overridesGenerics() { 275 checkOverridesInContainedClasses(TypesForGenerics.class); 276 } 277 278 @Test overridesRaw()279 public void overridesRaw() { 280 checkOverridesInContainedClasses(TypesForRaw.class); 281 } 282 283 // Test a tricky diamond inheritance hierarchy: 284 // Collection 285 // / \ 286 // AbstractCollection List 287 // \ / 288 // AbstractList 289 // This also tests that we do the right thing with generics, since naively the TypeMirror 290 // that you get for List<E> will not appear to be a subtype of the one you get for Collection<E> 291 // since the two Es are not the same. 292 @Test overridesDiamond()293 public void overridesDiamond() { 294 checkOverridesInSet(ImmutableSet.<Class<?>>of( 295 Collection.class, List.class, AbstractCollection.class, AbstractList.class)); 296 } 297 checkOverridesInContainedClasses(Class<?> container)298 private void checkOverridesInContainedClasses(Class<?> container) { 299 checkOverridesInSet(ImmutableSet.copyOf(container.getDeclaredClasses())); 300 } 301 checkOverridesInSet(ImmutableSet<Class<?>> testClasses)302 private void checkOverridesInSet(ImmutableSet<Class<?>> testClasses) { 303 assertThat(testClasses).isNotEmpty(); 304 ImmutableSet.Builder<TypeElement> testTypesBuilder = ImmutableSet.builder(); 305 for (Class<?> testClass : testClasses) { 306 testTypesBuilder.add(getTypeElement(testClass)); 307 } 308 ImmutableSet<TypeElement> testTypes = testTypesBuilder.build(); 309 ImmutableSet.Builder<ExecutableElement> testMethodsBuilder = ImmutableSet.builder(); 310 for (TypeElement testType : testTypes) { 311 testMethodsBuilder.addAll(methodsIn(testType.getEnclosedElements())); 312 } 313 ImmutableSet<ExecutableElement> testMethods = testMethodsBuilder.build(); 314 for (TypeElement in : testTypes) { 315 TypeElement javacIn = javacType(in); 316 List<ExecutableElement> inMethods = methodsIn(elementUtils.getAllMembers(in)); 317 for (ExecutableElement overrider : inMethods) { 318 ExecutableElement javacOverrider = javacMethod(overrider); 319 for (ExecutableElement overridden : testMethods) { 320 ExecutableElement javacOverridden = javacMethod(overridden); 321 boolean javacSays = javacOverrides.overrides(javacOverrider, javacOverridden, javacIn); 322 boolean weSay = explicitOverrides.overrides(overrider, overridden, in); 323 if (javacSays != weSay) { 324 expect 325 .withMessage( 326 "%s.%s overrides %s.%s in %s: javac says %s, we say %s", 327 overrider.getEnclosingElement(), overrider, 328 overridden.getEnclosingElement(), overridden, 329 in, 330 javacSays, weSay) 331 .fail(); 332 } 333 } 334 } 335 } 336 } 337 getTypeElement(Class<?> c)338 private TypeElement getTypeElement(Class<?> c) { 339 return elementUtils.getTypeElement(c.getCanonicalName()); 340 } 341 getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds)342 private ExecutableElement getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds) { 343 ExecutableElement found = null; 344 methods: 345 for (ExecutableElement method : methodsIn(in.getEnclosedElements())) { 346 if (method.getSimpleName().contentEquals(name) 347 && method.getParameters().size() == parameterTypeKinds.length) { 348 for (int i = 0; i < parameterTypeKinds.length; i++) { 349 if (method.getParameters().get(i).asType().getKind() != parameterTypeKinds[i]) { 350 continue methods; 351 } 352 } 353 assertThat(found).isNull(); 354 found = method; 355 } 356 } 357 assertThat(found).isNotNull(); 358 return found; 359 } 360 361 // These skeletal parallels to the real collection classes ensure that the test is independent 362 // of the details of those classes, for example whether List<E> redeclares add(E) even though 363 // it also inherits it from Collection<E>. 364 365 private interface XCollection<E> { add(E e)366 boolean add(E e); 367 } 368 369 private interface XList<E> extends XCollection<E> {} 370 371 private abstract static class XAbstractCollection<E> implements XCollection<E> { 372 @Override add(E e)373 public boolean add(E e) { 374 return false; 375 } 376 } 377 378 private abstract static class XAbstractList<E> 379 extends XAbstractCollection<E> implements XList<E> { 380 @Override add(E e)381 public boolean add(E e) { 382 return true; 383 } 384 } 385 386 private abstract static class XStringList extends XAbstractList<String> {} 387 388 private abstract static class XAbstractStringList implements XList<String> {} 389 390 private abstract static class XNumberList<E extends Number> extends XAbstractList<E> {} 391 392 // Parameter of add(E) in StringList is String. 393 // That means that we successfully recorded E[AbstractList] = String and E[List] = E[AbstractList] 394 // and String made it all the way through. 395 @Test methodParameters_StringList()396 public void methodParameters_StringList() { 397 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 398 TypeElement xStringList = getTypeElement(XStringList.class); 399 TypeElement string = getTypeElement(String.class); 400 401 ExecutableElement add = getMethod(xAbstractList, "add", TypeKind.TYPEVAR); 402 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xStringList); 403 List<TypeMirror> expectedParams = ImmutableList.of(string.asType()); 404 assertTypeListsEqual(params, expectedParams); 405 } 406 407 // Parameter of add(E) in AbstractStringList is String. 408 // That means that we successfully recorded E[List] = String and E[Collection] = E[List]. 409 @Test methodParameters_AbstractStringList()410 public void methodParameters_AbstractStringList() { 411 TypeElement xCollection = getTypeElement(XCollection.class); 412 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 413 TypeElement string = getTypeElement(String.class); 414 415 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 416 417 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xAbstractStringList); 418 List<TypeMirror> expectedParams = ImmutableList.of(string.asType()); 419 assertTypeListsEqual(params, expectedParams); 420 } 421 422 // Parameter of add(E) in NumberList is Number. 423 // That means that we successfully recorded E[AbstractList] = Number and on from 424 // there, with Number being used because it is the erasure of <E extends Number>. 425 @Test methodParams_NumberList()426 public void methodParams_NumberList() { 427 TypeElement xCollection = getTypeElement(XCollection.class); 428 TypeElement xNumberList = getTypeElement(XNumberList.class); 429 TypeElement number = getTypeElement(Number.class); 430 431 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 432 433 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xNumberList); 434 List<TypeMirror> expectedParams = ImmutableList.of(number.asType()); 435 assertTypeListsEqual(params, expectedParams); 436 } 437 438 // This is derived from a class that provoked a StackOverflowError in an earlier version. 439 private abstract static class StringToRangeConverter<T extends Comparable<T>> 440 extends Converter<String, Range<T>> { 441 @Override doBackward(Range<T> b)442 protected String doBackward(Range<T> b) { 443 return null; 444 } 445 } 446 447 @Test methodParams_RecursiveBound()448 public void methodParams_RecursiveBound() { 449 TypeElement stringToRangeConverter = getTypeElement(StringToRangeConverter.class); 450 TypeElement range = getTypeElement(Range.class); 451 ExecutableElement valueConverter = 452 getMethod(stringToRangeConverter, "doBackward", TypeKind.DECLARED); 453 List<TypeMirror> params = 454 explicitOverrides.erasedParameterTypes(valueConverter, stringToRangeConverter); 455 List<TypeMirror> expectedParams = 456 ImmutableList.<TypeMirror>of(typeUtils.erasure(range.asType())); 457 assertTypeListsEqual(params, expectedParams); 458 } 459 460 @Test methodFromSuperclasses()461 public void methodFromSuperclasses() { 462 TypeElement xAbstractCollection = getTypeElement(XAbstractCollection.class); 463 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 464 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 465 TypeElement xStringList = getTypeElement(XStringList.class); 466 467 ExecutableElement add = getMethod(xAbstractCollection, "add", TypeKind.TYPEVAR); 468 469 ExecutableElement addInAbstractStringList = 470 explicitOverrides.methodFromSuperclasses(xAbstractStringList, add); 471 assertThat(addInAbstractStringList).isNull(); 472 473 ExecutableElement addInStringList = 474 explicitOverrides.methodFromSuperclasses(xStringList, add); 475 assertThat(addInStringList.getEnclosingElement()).isEqualTo(xAbstractList); 476 } 477 478 @Test methodFromSuperinterfaces()479 public void methodFromSuperinterfaces() { 480 TypeElement xCollection = getTypeElement(XCollection.class); 481 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 482 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 483 TypeElement xNumberList = getTypeElement(XNumberList.class); 484 TypeElement xList = getTypeElement(XList.class); 485 486 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 487 488 ExecutableElement addInAbstractStringList = 489 explicitOverrides.methodFromSuperinterfaces(xAbstractStringList, add); 490 assertThat(addInAbstractStringList.getEnclosingElement()).isEqualTo(xCollection); 491 492 ExecutableElement addInNumberList = 493 explicitOverrides.methodFromSuperinterfaces(xNumberList, add); 494 assertThat(addInNumberList.getEnclosingElement()).isEqualTo(xAbstractList); 495 496 ExecutableElement addInList = 497 explicitOverrides.methodFromSuperinterfaces(xList, add); 498 assertThat(addInList.getEnclosingElement()).isEqualTo(xCollection); 499 } 500 assertTypeListsEqual(List<TypeMirror> actual, List<TypeMirror> expected)501 private void assertTypeListsEqual(List<TypeMirror> actual, List<TypeMirror> expected) { 502 assertThat(actual.size()).isEqualTo(expected.size()); 503 for (int i = 0; i < actual.size(); i++) { 504 assertThat(typeUtils.isSameType(actual.get(i), expected.get(i))).isTrue(); 505 } 506 } 507 508 // TODO(emcmanus): replace this with something from compile-testing when that's available. 509 /** 510 * An equivalent to {@link CompilationRule} that uses ecj (the Eclipse compiler) instead of javac. 511 * If the parameterized test is not selecting ecj then this rule has no effect. 512 */ 513 public class EcjCompilationRule implements TestRule { 514 Elements elements; 515 Types types; 516 517 @Override apply(Statement base, Description description)518 public Statement apply(Statement base, Description description) { 519 if (!compilerType.equals(CompilerType.ECJ)) { 520 return base; 521 } 522 return new EcjCompilationStatement(base); 523 } 524 } 525 526 private class EcjCompilationStatement extends Statement { 527 private final Statement statement; 528 EcjCompilationStatement(Statement base)529 EcjCompilationStatement(Statement base) { 530 this.statement = base; 531 } 532 533 @Override evaluate()534 public void evaluate() throws Throwable { 535 File tmpDir = File.createTempFile("OverridesTest", "dir"); 536 tmpDir.delete(); 537 tmpDir.mkdir(); 538 File dummySourceFile = new File(tmpDir, "Dummy.java"); 539 try { 540 Files.asCharSink(dummySourceFile, UTF_8).write("class Dummy {}"); 541 evaluate(dummySourceFile); 542 } finally { 543 dummySourceFile.delete(); 544 tmpDir.delete(); 545 } 546 } 547 evaluate(File dummySourceFile)548 private void evaluate(File dummySourceFile) throws Throwable { 549 JavaCompiler compiler = new EclipseCompiler(); 550 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, UTF_8); 551 // This hack is only needed in a Google-internal Java 8 environment where symbolic links make 552 // it hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless. 553 File rtJar = new File(StandardSystemProperty.JAVA_HOME.value() + "/lib/rt.jar"); 554 if (rtJar.exists()) { 555 List<File> bootClassPath = ImmutableList.<File>builder() 556 .add(rtJar) 557 .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH)) 558 .build(); 559 fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath); 560 } 561 Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjects(dummySourceFile); 562 JavaCompiler.CompilationTask task = 563 compiler.getTask(null, fileManager, null, null, null, sources); 564 EcjTestProcessor processor = new EcjTestProcessor(statement); 565 task.setProcessors(ImmutableList.of(processor)); 566 assertThat(task.call()).isTrue(); 567 processor.maybeThrow(); 568 } 569 } 570 571 @SupportedAnnotationTypes("*") 572 private class EcjTestProcessor extends AbstractProcessor { 573 private final Statement statement; 574 private Throwable thrown; 575 EcjTestProcessor(Statement statement)576 EcjTestProcessor(Statement statement) { 577 this.statement = statement; 578 } 579 580 @Override getSupportedSourceVersion()581 public SourceVersion getSupportedSourceVersion() { 582 return SourceVersion.latest(); 583 } 584 585 @Override process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)586 public boolean process( 587 Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 588 if (roundEnv.processingOver()) { 589 ecjCompilation.elements = processingEnv.getElementUtils(); 590 ecjCompilation.types = processingEnv.getTypeUtils(); 591 try { 592 statement.evaluate(); 593 } catch (Throwable t) { 594 thrown = t; 595 } 596 } 597 return false; 598 } 599 maybeThrow()600 void maybeThrow() throws Throwable { 601 if (thrown != null) { 602 throw thrown; 603 } 604 } 605 } 606 javacType(TypeElement type)607 private TypeElement javacType(TypeElement type) { 608 return javacElementUtils.getTypeElement(type.getQualifiedName().toString()); 609 } 610 javacMethod(ExecutableElement method)611 private ExecutableElement javacMethod(ExecutableElement method) { 612 if (elementUtils == javacElementUtils) { 613 return method; 614 } 615 TypeElement containingType = MoreElements.asType(method.getEnclosingElement()); 616 TypeElement javacContainingType = javacType(containingType); 617 List<ExecutableElement> candidates = new ArrayList<ExecutableElement>(); 618 methods: 619 for (ExecutableElement javacMethod : methodsIn(javacContainingType.getEnclosedElements())) { 620 if (javacMethod.getSimpleName().contentEquals(method.getSimpleName()) 621 && javacMethod.getParameters().size() == method.getParameters().size()) { 622 for (int i = 0; i < method.getParameters().size(); i++) { 623 VariableElement parameter = method.getParameters().get(i); 624 VariableElement javacParameter = javacMethod.getParameters().get(i); 625 if (!erasedToString(parameter.asType()).equals(erasedToString(javacParameter.asType()))) { 626 continue methods; 627 } 628 } 629 candidates.add(javacMethod); 630 } 631 } 632 if (candidates.size() == 1) { 633 return candidates.get(0); 634 } else { 635 throw new IllegalStateException( 636 "Expected one javac method matching " + method + " but found " + candidates); 637 } 638 } 639 erasedToString(TypeMirror type)640 private static String erasedToString(TypeMirror type) { 641 return ERASED_STRING_TYPE_VISITOR.visit(type); 642 } 643 644 private static final TypeVisitor<String, Void> ERASED_STRING_TYPE_VISITOR = 645 new SimpleTypeVisitor6<String, Void>() { 646 @Override 647 protected String defaultAction(TypeMirror e, Void p) { 648 return e.toString(); 649 } 650 651 @Override 652 public String visitArray(ArrayType t, Void p) { 653 return visit(t.getComponentType()) + "[]"; 654 } 655 656 @Override 657 public String visitDeclared(DeclaredType t, Void p) { 658 return MoreElements.asType(t.asElement()).getQualifiedName().toString(); 659 } 660 661 @Override 662 public String visitTypeVariable(TypeVariable t, Void p) { 663 return visit(t.getUpperBound()); 664 } 665 }; 666 } 667