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.Correspondence; 31 import com.google.common.truth.Expect; 32 import com.google.testing.compile.CompilationRule; 33 import java.io.File; 34 import java.util.AbstractCollection; 35 import java.util.AbstractList; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.List; 39 import java.util.Set; 40 import javax.annotation.processing.AbstractProcessor; 41 import javax.annotation.processing.RoundEnvironment; 42 import javax.annotation.processing.SupportedAnnotationTypes; 43 import javax.lang.model.SourceVersion; 44 import javax.lang.model.element.Element; 45 import javax.lang.model.element.ExecutableElement; 46 import javax.lang.model.element.TypeElement; 47 import javax.lang.model.element.VariableElement; 48 import javax.lang.model.type.ArrayType; 49 import javax.lang.model.type.DeclaredType; 50 import javax.lang.model.type.TypeKind; 51 import javax.lang.model.type.TypeMirror; 52 import javax.lang.model.type.TypeVariable; 53 import javax.lang.model.type.TypeVisitor; 54 import javax.lang.model.util.Elements; 55 import javax.lang.model.util.SimpleTypeVisitor6; 56 import javax.lang.model.util.Types; 57 import javax.tools.JavaCompiler; 58 import javax.tools.JavaFileObject; 59 import javax.tools.StandardJavaFileManager; 60 import javax.tools.StandardLocation; 61 import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; 62 import org.checkerframework.checker.nullness.qual.Nullable; 63 import org.junit.Before; 64 import org.junit.Rule; 65 import org.junit.Test; 66 import org.junit.rules.TestRule; 67 import org.junit.runner.Description; 68 import org.junit.runner.RunWith; 69 import org.junit.runners.Parameterized; 70 import org.junit.runners.model.Statement; 71 72 /** 73 * Tests that the {@link Overrides} class has behaviour consistent with javac. We test this in 74 * two ways: once with {@link Overrides.ExplicitOverrides} using javac's own {@link Elements} and 75 * {@link Types}, and once with it using the version of those objects from the Eclipse compiler 76 * (ecj). 77 * 78 * @author emcmanus@google.com (Éamonn McManus) 79 */ 80 @RunWith(Parameterized.class) 81 public class OverridesTest { 82 @Parameterized.Parameters(name = "{0}") data()83 public static CompilerType[] data() { 84 return CompilerType.values(); 85 } 86 87 @Rule public CompilationRule compilation = new CompilationRule(); 88 @Rule public EcjCompilationRule ecjCompilation = new EcjCompilationRule(); 89 @Rule public Expect expect = Expect.create(); 90 91 public enum CompilerType { 92 JAVAC { 93 @Override initUtils(OverridesTest test)94 void initUtils(OverridesTest test) { 95 test.typeUtils = test.compilation.getTypes(); 96 test.elementUtils = test.compilation.getElements(); 97 } 98 }, 99 ECJ { 100 @Override initUtils(OverridesTest test)101 void initUtils(OverridesTest test) { 102 test.typeUtils = test.ecjCompilation.types; 103 test.elementUtils = test.ecjCompilation.elements; 104 } 105 }; 106 initUtils(OverridesTest test)107 abstract void initUtils(OverridesTest test); 108 } 109 110 private final CompilerType compilerType; 111 112 private Types typeUtils; 113 private Elements elementUtils; 114 private Elements javacElementUtils; 115 private Overrides javacOverrides; 116 private Overrides.ExplicitOverrides explicitOverrides; 117 OverridesTest(CompilerType compilerType)118 public OverridesTest(CompilerType compilerType) { 119 this.compilerType = compilerType; 120 } 121 122 @Before initializeTestElements()123 public void initializeTestElements() { 124 javacElementUtils = compilation.getElements(); 125 javacOverrides = new Overrides.NativeOverrides(javacElementUtils); 126 compilerType.initUtils(this); 127 explicitOverrides = new Overrides.ExplicitOverrides(typeUtils); 128 } 129 130 static class TypesForInheritance { 131 interface One { m()132 void m(); 133 m(String x)134 void m(String x); 135 n()136 void n(); 137 number()138 Number number(); 139 } 140 141 interface Two { m()142 void m(); 143 m(int x)144 void m(int x); 145 number()146 Integer number(); 147 } 148 149 static class Parent { m()150 public void m() {} 151 } 152 153 static class ChildOfParent extends Parent {} 154 155 static class ChildOfOne implements One { 156 @Override m()157 public void m() {} 158 159 @Override m(String x)160 public void m(String x) {} 161 162 @Override n()163 public void n() {} 164 165 @Override number()166 public Number number() { 167 return 0; 168 } 169 } 170 171 static class ChildOfOneAndTwo implements One, Two { 172 @Override m()173 public void m() {} 174 175 @Override m(String x)176 public void m(String x) {} 177 178 @Override m(int x)179 public void m(int x) {} 180 181 @Override n()182 public void n() {} 183 184 @Override number()185 public Integer number() { 186 return 0; 187 } 188 } 189 190 static class ChildOfParentAndOne extends Parent implements One { 191 @Override m()192 public void m() {} 193 194 @Override m(String x)195 public void m(String x) {} 196 197 @Override n()198 public void n() {} 199 200 @Override number()201 public Number number() { 202 return 0; 203 } 204 } 205 206 static class ChildOfParentAndOneAndTwo extends Parent implements One, Two { 207 @Override m(String x)208 public void m(String x) {} 209 210 @Override m(int x)211 public void m(int x) {} 212 213 @Override n()214 public void n() {} 215 216 @Override number()217 public Integer number() { 218 return 0; 219 } 220 } 221 222 abstract static class AbstractChildOfOne implements One {} 223 224 abstract static class AbstractChildOfOneAndTwo implements One, Two {} 225 226 abstract static class AbstractChildOfParentAndOneAndTwo extends Parent implements One, Two {} 227 228 interface ExtendingOneAndTwo extends One, Two {} 229 } 230 231 static class MoreTypesForInheritance { 232 interface Key {} 233 234 interface BindingType {} 235 236 interface ContributionType {} 237 238 interface HasKey { key()239 Key key(); 240 } 241 242 interface HasBindingType { bindingType()243 BindingType bindingType(); 244 } 245 246 interface HasContributionType { contributionType()247 ContributionType contributionType(); 248 } 249 250 abstract static class BindingDeclaration implements HasKey { bindingElement()251 abstract Optional<Element> bindingElement(); 252 contributingModule()253 abstract Optional<TypeElement> contributingModule(); 254 } 255 256 abstract static class MultibindingDeclaration extends BindingDeclaration 257 implements HasBindingType, HasContributionType { 258 @Override key()259 public abstract Key key(); 260 261 @Override contributionType()262 public abstract ContributionType contributionType(); 263 264 @Override bindingType()265 public abstract BindingType bindingType(); 266 } 267 } 268 269 static class TypesForVisibility { 270 public abstract static class PublicGrandparent { foo()271 public abstract String foo(); 272 } 273 274 private static class PrivateParent extends PublicGrandparent { 275 @Override foo()276 public String foo() { 277 return "foo"; 278 } 279 } 280 281 static class Child extends PrivateParent {} 282 } 283 284 static class TypesForGenerics { 285 interface GCollection<E> { add(E x)286 boolean add(E x); 287 } 288 289 interface GList<E> extends GCollection<E> { 290 @Override add(E x)291 boolean add(E x); 292 } 293 294 static class StringList implements GList<String> { 295 @Override add(String x)296 public boolean add(String x) { 297 return false; 298 } 299 } 300 301 @SuppressWarnings("rawtypes") 302 static class RawList implements GList { 303 @Override add(Object x)304 public boolean add(Object x) { 305 return false; 306 } 307 } 308 } 309 310 @SuppressWarnings("rawtypes") 311 static class TypesForRaw { 312 static class RawParent { frob(List x)313 void frob(List x) {} 314 } 315 316 static class RawChildOfRaw extends RawParent { 317 @Override frob(List x)318 void frob(List x) {} 319 } 320 321 static class NonRawParent { frob(List<String> x)322 void frob(List<String> x) {} 323 } 324 325 static class RawChildOfNonRaw extends NonRawParent { 326 @Override frob(List x)327 void frob(List x) {} 328 } 329 } 330 331 @Test overridesInheritance()332 public void overridesInheritance() { 333 checkOverridesInContainedClasses(TypesForInheritance.class); 334 } 335 336 @Test overridesMoreInheritance()337 public void overridesMoreInheritance() { 338 checkOverridesInContainedClasses(MoreTypesForInheritance.class); 339 } 340 341 @Test overridesVisibility()342 public void overridesVisibility() { 343 checkOverridesInContainedClasses(TypesForVisibility.class); 344 } 345 346 @Test overridesGenerics()347 public void overridesGenerics() { 348 checkOverridesInContainedClasses(TypesForGenerics.class); 349 } 350 351 @Test overridesRaw()352 public void overridesRaw() { 353 checkOverridesInContainedClasses(TypesForRaw.class); 354 } 355 356 // Test a tricky diamond inheritance hierarchy: 357 // Collection 358 // / \ 359 // AbstractCollection List 360 // \ / 361 // AbstractList 362 // This also tests that we do the right thing with generics, since naively the TypeMirror 363 // that you get for List<E> will not appear to be a subtype of the one you get for Collection<E> 364 // since the two Es are not the same. 365 @Test overridesDiamond()366 public void overridesDiamond() { 367 checkOverridesInSet( 368 ImmutableSet.<Class<?>>of( 369 Collection.class, List.class, AbstractCollection.class, AbstractList.class)); 370 } 371 checkOverridesInContainedClasses(Class<?> container)372 private void checkOverridesInContainedClasses(Class<?> container) { 373 checkOverridesInSet(ImmutableSet.copyOf(container.getDeclaredClasses())); 374 } 375 checkOverridesInSet(ImmutableSet<Class<?>> testClasses)376 private void checkOverridesInSet(ImmutableSet<Class<?>> testClasses) { 377 assertThat(testClasses).isNotEmpty(); 378 ImmutableSet.Builder<TypeElement> testTypesBuilder = ImmutableSet.builder(); 379 for (Class<?> testClass : testClasses) { 380 testTypesBuilder.add(getTypeElement(testClass)); 381 } 382 ImmutableSet<TypeElement> testTypes = testTypesBuilder.build(); 383 ImmutableSet.Builder<ExecutableElement> testMethodsBuilder = ImmutableSet.builder(); 384 for (TypeElement testType : testTypes) { 385 testMethodsBuilder.addAll(methodsIn(testType.getEnclosedElements())); 386 } 387 ImmutableSet<ExecutableElement> testMethods = testMethodsBuilder.build(); 388 for (TypeElement in : testTypes) { 389 TypeElement javacIn = javacType(in); 390 List<ExecutableElement> inMethods = methodsIn(elementUtils.getAllMembers(in)); 391 for (ExecutableElement overrider : inMethods) { 392 ExecutableElement javacOverrider = javacMethod(overrider); 393 for (ExecutableElement overridden : testMethods) { 394 ExecutableElement javacOverridden = javacMethod(overridden); 395 boolean javacSays = javacOverrides.overrides(javacOverrider, javacOverridden, javacIn); 396 boolean weSay = explicitOverrides.overrides(overrider, overridden, in); 397 if (javacSays != weSay) { 398 expect 399 .withMessage( 400 "%s.%s overrides %s.%s in %s: javac says %s, we say %s", 401 overrider.getEnclosingElement(), 402 overrider, 403 overridden.getEnclosingElement(), 404 overridden, 405 in, 406 javacSays, 407 weSay) 408 .fail(); 409 } 410 } 411 } 412 } 413 } 414 getTypeElement(Class<?> c)415 private TypeElement getTypeElement(Class<?> c) { 416 return elementUtils.getTypeElement(c.getCanonicalName()); 417 } 418 getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds)419 private ExecutableElement getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds) { 420 ExecutableElement found = null; 421 methods: 422 for (ExecutableElement method : methodsIn(in.getEnclosedElements())) { 423 if (method.getSimpleName().contentEquals(name) 424 && method.getParameters().size() == parameterTypeKinds.length) { 425 for (int i = 0; i < parameterTypeKinds.length; i++) { 426 if (method.getParameters().get(i).asType().getKind() != parameterTypeKinds[i]) { 427 continue methods; 428 } 429 } 430 assertThat(found).isNull(); 431 found = method; 432 } 433 } 434 assertThat(found).isNotNull(); 435 return requireNonNull(found); 436 } 437 438 // These skeletal parallels to the real collection classes ensure that the test is independent 439 // of the details of those classes, for example whether List<E> redeclares add(E) even though 440 // it also inherits it from Collection<E>. 441 442 private interface XCollection<E> { add(E e)443 boolean add(E e); 444 } 445 446 private interface XList<E> extends XCollection<E> {} 447 448 private abstract static class XAbstractCollection<E> implements XCollection<E> { 449 @Override add(E e)450 public boolean add(E e) { 451 return false; 452 } 453 } 454 455 private abstract static class XAbstractList<E> extends XAbstractCollection<E> 456 implements XList<E> { 457 @Override add(E e)458 public boolean add(E e) { 459 return true; 460 } 461 } 462 463 private abstract static class XStringList extends XAbstractList<String> {} 464 465 private abstract static class XAbstractStringList implements XList<String> {} 466 467 private abstract static class XNumberList<E extends Number> extends XAbstractList<E> {} 468 469 // Parameter of add(E) in StringList is String. 470 // That means that we successfully recorded E[AbstractList] = String and E[List] = E[AbstractList] 471 // and String made it all the way through. 472 @Test methodParameters_StringList()473 public void methodParameters_StringList() { 474 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 475 TypeElement xStringList = getTypeElement(XStringList.class); 476 TypeElement string = getTypeElement(String.class); 477 478 ExecutableElement add = getMethod(xAbstractList, "add", TypeKind.TYPEVAR); 479 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xStringList); 480 List<TypeMirror> expectedParams = ImmutableList.of(string.asType()); 481 assertTypeListsEqual(params, expectedParams); 482 } 483 484 // Parameter of add(E) in AbstractStringList is String. 485 // That means that we successfully recorded E[List] = String and E[Collection] = E[List]. 486 @Test methodParameters_AbstractStringList()487 public void methodParameters_AbstractStringList() { 488 TypeElement xCollection = getTypeElement(XCollection.class); 489 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 490 TypeElement string = getTypeElement(String.class); 491 492 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 493 494 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xAbstractStringList); 495 List<TypeMirror> expectedParams = ImmutableList.of(string.asType()); 496 assertTypeListsEqual(params, expectedParams); 497 } 498 499 // Parameter of add(E) in NumberList is Number. 500 // That means that we successfully recorded E[AbstractList] = Number and on from 501 // there, with Number being used because it is the erasure of <E extends Number>. 502 @Test methodParams_NumberList()503 public void methodParams_NumberList() { 504 TypeElement xCollection = getTypeElement(XCollection.class); 505 TypeElement xNumberList = getTypeElement(XNumberList.class); 506 TypeElement number = getTypeElement(Number.class); 507 508 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 509 510 List<TypeMirror> params = explicitOverrides.erasedParameterTypes(add, xNumberList); 511 List<TypeMirror> expectedParams = ImmutableList.of(number.asType()); 512 assertTypeListsEqual(params, expectedParams); 513 } 514 515 // This is derived from a class that provoked a StackOverflowError in an earlier version. 516 private abstract static class StringToRangeConverter<T extends Comparable<T>> 517 extends Converter<String, Range<T>> { 518 @Override doBackward(Range<T> b)519 protected String doBackward(Range<T> b) { 520 return ""; 521 } 522 } 523 524 @Test methodParams_RecursiveBound()525 public void methodParams_RecursiveBound() { 526 TypeElement stringToRangeConverter = getTypeElement(StringToRangeConverter.class); 527 TypeElement range = getTypeElement(Range.class); 528 ExecutableElement valueConverter = 529 getMethod(stringToRangeConverter, "doBackward", TypeKind.DECLARED); 530 List<TypeMirror> params = 531 explicitOverrides.erasedParameterTypes(valueConverter, stringToRangeConverter); 532 List<TypeMirror> expectedParams = 533 ImmutableList.<TypeMirror>of(typeUtils.erasure(range.asType())); 534 assertTypeListsEqual(params, expectedParams); 535 } 536 537 @Test methodFromSuperclasses()538 public void methodFromSuperclasses() { 539 TypeElement xAbstractCollection = getTypeElement(XAbstractCollection.class); 540 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 541 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 542 TypeElement xStringList = getTypeElement(XStringList.class); 543 544 ExecutableElement add = getMethod(xAbstractCollection, "add", TypeKind.TYPEVAR); 545 546 ExecutableElement addInAbstractStringList = 547 explicitOverrides.methodFromSuperclasses(xAbstractStringList, add); 548 assertThat(addInAbstractStringList).isNull(); 549 550 ExecutableElement addInStringList = explicitOverrides.methodFromSuperclasses(xStringList, add); 551 assertThat(requireNonNull(addInStringList).getEnclosingElement()).isEqualTo(xAbstractList); 552 } 553 554 @Test methodFromSuperinterfaces()555 public void methodFromSuperinterfaces() { 556 TypeElement xCollection = getTypeElement(XCollection.class); 557 TypeElement xAbstractList = getTypeElement(XAbstractList.class); 558 TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); 559 TypeElement xNumberList = getTypeElement(XNumberList.class); 560 TypeElement xList = getTypeElement(XList.class); 561 562 ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); 563 564 ExecutableElement addInAbstractStringList = 565 explicitOverrides.methodFromSuperinterfaces(xAbstractStringList, add); 566 assertThat(requireNonNull(addInAbstractStringList).getEnclosingElement()) 567 .isEqualTo(xCollection); 568 569 ExecutableElement addInNumberList = 570 explicitOverrides.methodFromSuperinterfaces(xNumberList, add); 571 assertThat(requireNonNull(addInNumberList).getEnclosingElement()).isEqualTo(xAbstractList); 572 573 ExecutableElement addInList = explicitOverrides.methodFromSuperinterfaces(xList, add); 574 assertThat(requireNonNull(addInList).getEnclosingElement()).isEqualTo(xCollection); 575 } 576 assertTypeListsEqual(@ullable List<TypeMirror> actual, List<TypeMirror> expected)577 private void assertTypeListsEqual(@Nullable List<TypeMirror> actual, List<TypeMirror> expected) { 578 assertThat(actual) 579 .comparingElementsUsing(Correspondence.from(typeUtils::isSameType, "is same type as")) 580 .containsExactlyElementsIn(expected) 581 .inOrder(); 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