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