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