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