1 /* 2 * Copyright (C) 2014 The Dagger Authors. 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 17 package dagger.internal.codegen.validation; 18 19 import static androidx.room.compiler.processing.XTypeKt.isVoid; 20 import static com.google.common.base.Verify.verify; 21 import static com.google.common.collect.Iterables.consumingIterable; 22 import static com.google.common.collect.Iterables.getOnlyElement; 23 import static com.google.common.collect.Multimaps.asMap; 24 import static com.google.common.collect.Sets.intersection; 25 import static dagger.internal.codegen.base.ComponentAnnotation.anyComponentAnnotation; 26 import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation; 27 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor; 28 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.productionCreatorAnnotations; 29 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.subcomponentCreatorAnnotations; 30 import static dagger.internal.codegen.base.ComponentKind.annotationsFor; 31 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation; 32 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotations; 33 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; 34 import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes; 35 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs; 36 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent; 37 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 38 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 39 import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation; 40 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 41 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.javacOverrides; 42 import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods; 43 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 44 import static java.util.Comparator.comparing; 45 46 import androidx.room.compiler.processing.XAnnotation; 47 import androidx.room.compiler.processing.XExecutableParameterElement; 48 import androidx.room.compiler.processing.XMethodElement; 49 import androidx.room.compiler.processing.XMethodType; 50 import androidx.room.compiler.processing.XType; 51 import androidx.room.compiler.processing.XTypeElement; 52 import com.google.common.collect.HashMultimap; 53 import com.google.common.collect.ImmutableList; 54 import com.google.common.collect.ImmutableSet; 55 import com.google.common.collect.LinkedHashMultimap; 56 import com.google.common.collect.Maps; 57 import com.google.common.collect.SetMultimap; 58 import com.google.common.collect.Sets; 59 import com.squareup.javapoet.ClassName; 60 import com.squareup.javapoet.TypeName; 61 import dagger.Component; 62 import dagger.internal.codegen.base.ClearableCache; 63 import dagger.internal.codegen.base.ComponentAnnotation; 64 import dagger.internal.codegen.base.ComponentKind; 65 import dagger.internal.codegen.base.DaggerSuperficialValidation; 66 import dagger.internal.codegen.base.ModuleKind; 67 import dagger.internal.codegen.binding.DependencyRequestFactory; 68 import dagger.internal.codegen.binding.ErrorMessages; 69 import dagger.internal.codegen.binding.MethodSignatureFormatter; 70 import dagger.internal.codegen.javapoet.TypeNames; 71 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 72 import dagger.internal.codegen.model.DependencyRequest; 73 import dagger.internal.codegen.model.Key; 74 import dagger.internal.codegen.xprocessing.XTypeElements; 75 import dagger.internal.codegen.xprocessing.XTypes; 76 import java.util.ArrayDeque; 77 import java.util.Collection; 78 import java.util.HashMap; 79 import java.util.HashSet; 80 import java.util.List; 81 import java.util.Map; 82 import java.util.Optional; 83 import java.util.Queue; 84 import java.util.Set; 85 import javax.inject.Inject; 86 import javax.inject.Singleton; 87 import javax.lang.model.SourceVersion; 88 89 /** 90 * Performs superficial validation of the contract of the {@link Component} and {@link 91 * dagger.producers.ProductionComponent} annotations. 92 */ 93 @Singleton 94 public final class ComponentValidator implements ClearableCache { 95 private final ModuleValidator moduleValidator; 96 private final ComponentCreatorValidator creatorValidator; 97 private final DependencyRequestValidator dependencyRequestValidator; 98 private final MembersInjectionValidator membersInjectionValidator; 99 private final MethodSignatureFormatter methodSignatureFormatter; 100 private final DependencyRequestFactory dependencyRequestFactory; 101 private final DaggerSuperficialValidation superficialValidation; 102 private final Map<XTypeElement, ValidationReport> reports = new HashMap<>(); 103 private final KotlinMetadataUtil metadataUtil; 104 105 @Inject ComponentValidator( ModuleValidator moduleValidator, ComponentCreatorValidator creatorValidator, DependencyRequestValidator dependencyRequestValidator, MembersInjectionValidator membersInjectionValidator, MethodSignatureFormatter methodSignatureFormatter, DependencyRequestFactory dependencyRequestFactory, DaggerSuperficialValidation superficialValidation, KotlinMetadataUtil metadataUtil)106 ComponentValidator( 107 ModuleValidator moduleValidator, 108 ComponentCreatorValidator creatorValidator, 109 DependencyRequestValidator dependencyRequestValidator, 110 MembersInjectionValidator membersInjectionValidator, 111 MethodSignatureFormatter methodSignatureFormatter, 112 DependencyRequestFactory dependencyRequestFactory, 113 DaggerSuperficialValidation superficialValidation, 114 KotlinMetadataUtil metadataUtil) { 115 this.moduleValidator = moduleValidator; 116 this.creatorValidator = creatorValidator; 117 this.dependencyRequestValidator = dependencyRequestValidator; 118 this.membersInjectionValidator = membersInjectionValidator; 119 this.methodSignatureFormatter = methodSignatureFormatter; 120 this.dependencyRequestFactory = dependencyRequestFactory; 121 this.superficialValidation = superficialValidation; 122 this.metadataUtil = metadataUtil; 123 } 124 125 @Override clearCache()126 public void clearCache() { 127 reports.clear(); 128 } 129 130 /** Validates the given component. */ validate(XTypeElement component)131 public ValidationReport validate(XTypeElement component) { 132 return reentrantComputeIfAbsent(reports, component, this::validateUncached); 133 } 134 validateUncached(XTypeElement component)135 private ValidationReport validateUncached(XTypeElement component) { 136 return new ElementValidator(component).validateElement(); 137 } 138 139 private class ElementValidator { 140 private final XTypeElement component; 141 private final ValidationReport.Builder report; 142 private final ImmutableSet<ComponentKind> componentKinds; 143 144 // Populated by ComponentMethodValidators 145 private final SetMultimap<XTypeElement, XMethodElement> referencedSubcomponents = 146 LinkedHashMultimap.create(); 147 ElementValidator(XTypeElement component)148 ElementValidator(XTypeElement component) { 149 this.component = component; 150 this.report = ValidationReport.about(component); 151 this.componentKinds = ComponentKind.getComponentKinds(component); 152 } 153 componentKind()154 private ComponentKind componentKind() { 155 return getOnlyElement(componentKinds); 156 } 157 componentAnnotation()158 private ComponentAnnotation componentAnnotation() { 159 return anyComponentAnnotation(component, superficialValidation).get(); 160 } 161 validateElement()162 ValidationReport validateElement() { 163 if (componentKinds.size() > 1) { 164 return report.addError(moreThanOneComponentAnnotationError(), component).build(); 165 } 166 if (!(component.isInterface() || component.isClass())) { 167 // If the annotated element is not a class or interface skip the rest of the checks since 168 // the remaining checks will likely just output unhelpful noise in such cases. 169 return report.addError(invalidTypeError(), component).build(); 170 } 171 validateFields(); 172 validateUseOfCancellationPolicy(); 173 validateIsAbstractType(); 174 validateCreators(); 175 validateNoReusableAnnotation(); 176 validateComponentMethods(); 177 validateNoConflictingEntryPoints(); 178 validateSubcomponentReferences(); 179 validateComponentDependencies(); 180 validateReferencedModules(); 181 validateSubcomponents(); 182 183 return report.build(); 184 } 185 moreThanOneComponentAnnotationError()186 private String moreThanOneComponentAnnotationError() { 187 return String.format( 188 "Components may not be annotated with more than one component annotation: found %s", 189 annotationsFor(componentKinds)); 190 } 191 validateUseOfCancellationPolicy()192 private void validateUseOfCancellationPolicy() { 193 if (component.hasAnnotation(TypeNames.CANCELLATION_POLICY) && !componentKind().isProducer()) { 194 report.addError( 195 "@CancellationPolicy may only be applied to production components and subcomponents", 196 component); 197 } 198 } 199 validateIsAbstractType()200 private void validateIsAbstractType() { 201 if (!component.isAbstract()) { 202 report.addError(invalidTypeError(), component); 203 } 204 } 205 invalidTypeError()206 private String invalidTypeError() { 207 return String.format( 208 "@%s may only be applied to an interface or abstract class", 209 componentKind().annotation().simpleName()); 210 } 211 validateFields()212 private void validateFields() { 213 component.getDeclaredMethods().stream() 214 .filter(method -> method.isKotlinPropertySetter() && method.isAbstract()) 215 .forEach( 216 method -> 217 report.addError( 218 String.format( 219 "Cannot use 'abstract var' property in a component declaration to get a" 220 + " binding. Use 'val' or 'fun' instead: %s", 221 method.getPropertyName()))); 222 } 223 validateCreators()224 private void validateCreators() { 225 ImmutableSet<XTypeElement> creators = 226 enclosedAnnotatedTypes(component, creatorAnnotationsFor(componentAnnotation())); 227 creators.forEach(creator -> report.addSubreport(creatorValidator.validate(creator))); 228 if (creators.size() > 1) { 229 report.addError( 230 String.format( 231 ErrorMessages.componentMessagesFor(componentKind()).moreThanOne(), 232 creators.stream().map(XTypeElement::getQualifiedName).collect(toImmutableSet())), 233 component); 234 } 235 } 236 validateNoReusableAnnotation()237 private void validateNoReusableAnnotation() { 238 if (component.hasAnnotation(TypeNames.REUSABLE)) { 239 report.addError( 240 "@Reusable cannot be applied to components or subcomponents", 241 component, 242 component.getAnnotation(TypeNames.REUSABLE)); 243 } 244 } 245 validateComponentMethods()246 private void validateComponentMethods() { 247 validateClassMethodName(); 248 getAllUnimplementedMethods(component).stream() 249 .map(ComponentMethodValidator::new) 250 .forEachOrdered(ComponentMethodValidator::validateMethod); 251 } 252 validateClassMethodName()253 private void validateClassMethodName() { 254 if (metadataUtil.hasMetadata(component)) { 255 metadataUtil 256 .getAllMethodNamesBySignature(component) 257 .forEach( 258 (signature, name) -> { 259 if (SourceVersion.isKeyword(name)) { 260 report.addError("Can not use a Java keyword as method name: " + signature); 261 } 262 }); 263 } 264 } 265 266 private class ComponentMethodValidator { 267 private final XMethodElement method; 268 private final XMethodType resolvedMethod; 269 private final List<XType> parameterTypes; 270 private final List<XExecutableParameterElement> parameters; 271 private final XType returnType; 272 ComponentMethodValidator(XMethodElement method)273 ComponentMethodValidator(XMethodElement method) { 274 this.method = method; 275 this.resolvedMethod = method.asMemberOf(component.getType()); 276 this.parameterTypes = resolvedMethod.getParameterTypes(); 277 this.parameters = method.getParameters(); 278 this.returnType = resolvedMethod.getReturnType(); 279 } 280 validateMethod()281 void validateMethod() { 282 validateNoTypeVariables(); 283 284 // abstract methods are ones we have to implement, so they each need to be validated 285 // first, check the return type. if it's a subcomponent, validate that method as 286 // such. 287 Optional<ComponentAnnotation> subcomponentAnnotation = legalSubcomponentAnnotation(); 288 if (subcomponentAnnotation.isPresent()) { 289 validateSubcomponentFactoryMethod(subcomponentAnnotation.get()); 290 } else if (subcomponentCreatorAnnotation().isPresent()) { 291 validateSubcomponentCreatorMethod(); 292 } else { 293 // if it's not a subcomponent... 294 switch (parameters.size()) { 295 case 0: 296 validateProvisionMethod(); 297 break; 298 case 1: 299 validateMembersInjectionMethod(); 300 break; 301 default: 302 reportInvalidMethod(); 303 break; 304 } 305 } 306 } 307 validateNoTypeVariables()308 private void validateNoTypeVariables() { 309 if (!resolvedMethod.getTypeVariableNames().isEmpty()) { 310 report.addError("Component methods cannot have type variables", method); 311 } 312 } 313 legalSubcomponentAnnotation()314 private Optional<ComponentAnnotation> legalSubcomponentAnnotation() { 315 return Optional.ofNullable(returnType.getTypeElement()) 316 .flatMap(element -> subcomponentAnnotation(element, superficialValidation)) 317 // TODO(bcorso): Consider failing on illegal subcomponents rather than just filtering. 318 .filter(annotation -> legalSubcomponentAnnotations().contains(annotation.className())); 319 } 320 legalSubcomponentAnnotations()321 private ImmutableSet<ClassName> legalSubcomponentAnnotations() { 322 return componentKind().legalSubcomponentKinds().stream() 323 .map(ComponentKind::annotation) 324 .collect(toImmutableSet()); 325 } 326 subcomponentCreatorAnnotation()327 private Optional<XAnnotation> subcomponentCreatorAnnotation() { 328 return checkForAnnotations( 329 returnType, 330 componentAnnotation().isProduction() 331 ? intersection(subcomponentCreatorAnnotations(), productionCreatorAnnotations()) 332 : subcomponentCreatorAnnotations()); 333 } 334 validateSubcomponentFactoryMethod(ComponentAnnotation subcomponentAnnotation)335 private void validateSubcomponentFactoryMethod(ComponentAnnotation subcomponentAnnotation) { 336 referencedSubcomponents.put(returnType.getTypeElement(), method); 337 338 ImmutableSet<ClassName> legalModuleAnnotations = 339 ComponentKind.forAnnotatedElement(returnType.getTypeElement()) 340 .get() 341 .legalModuleKinds() 342 .stream() 343 .map(ModuleKind::annotation) 344 .collect(toImmutableSet()); 345 ImmutableSet<XTypeElement> moduleTypes = subcomponentAnnotation.modules(); 346 347 // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse 348 // subcomponents and their modules separately from how it is done in ComponentDescriptor and 349 // ModuleDescriptor 350 ImmutableSet<XTypeElement> transitiveModules = getTransitiveModules(moduleTypes); 351 352 Set<XTypeElement> referencedModules = Sets.newHashSet(); 353 for (int i = 0; i < parameterTypes.size(); i++) { 354 XExecutableParameterElement parameter = parameters.get(i); 355 XType parameterType = parameterTypes.get(i); 356 if (checkForAnnotations(parameterType, legalModuleAnnotations).isPresent()) { 357 XTypeElement module = parameterType.getTypeElement(); 358 if (referencedModules.contains(module)) { 359 report.addError( 360 String.format( 361 "A module may only occur once as an argument in a Subcomponent factory " 362 + "method, but %s was already passed.", 363 module.getQualifiedName()), 364 parameter); 365 } 366 if (!transitiveModules.contains(module)) { 367 report.addError( 368 String.format( 369 "%s is present as an argument to the %s factory method, but is not one of the" 370 + " modules used to implement the subcomponent.", 371 module.getQualifiedName(), returnType.getTypeElement().getQualifiedName()), 372 method); 373 } 374 referencedModules.add(module); 375 } else { 376 report.addError( 377 String.format( 378 "Subcomponent factory methods may only accept modules, but %s is not.", 379 XTypes.toStableString(parameterType)), 380 parameter); 381 } 382 } 383 } 384 385 /** 386 * Returns the full set of modules transitively included from the given seed modules, which 387 * includes all transitive {@link Module#includes} and all transitive super classes. If a 388 * module is malformed and a type listed in {@link Module#includes} is not annotated with 389 * {@link Module}, it is ignored. 390 */ getTransitiveModules( Collection<XTypeElement> seedModules)391 private ImmutableSet<XTypeElement> getTransitiveModules( 392 Collection<XTypeElement> seedModules) { 393 Set<XTypeElement> processedElements = Sets.newLinkedHashSet(); 394 Queue<XTypeElement> moduleQueue = new ArrayDeque<>(seedModules); 395 ImmutableSet.Builder<XTypeElement> moduleElements = ImmutableSet.builder(); 396 for (XTypeElement moduleElement : consumingIterable(moduleQueue)) { 397 if (processedElements.add(moduleElement)) { 398 moduleAnnotation(moduleElement, superficialValidation) 399 .ifPresent( 400 moduleAnnotation -> { 401 moduleElements.add(moduleElement); 402 moduleQueue.addAll(moduleAnnotation.includes()); 403 moduleQueue.addAll(includesFromSuperclasses(moduleElement)); 404 }); 405 } 406 } 407 return moduleElements.build(); 408 } 409 410 /** Returns {@link Module#includes()} from all transitive super classes. */ includesFromSuperclasses(XTypeElement element)411 private ImmutableSet<XTypeElement> includesFromSuperclasses(XTypeElement element) { 412 ImmutableSet.Builder<XTypeElement> builder = ImmutableSet.builder(); 413 XType superclass = element.getSuperType(); 414 while (superclass != null && !TypeName.OBJECT.equals(superclass.getTypeName())) { 415 element = superclass.getTypeElement(); 416 moduleAnnotation(element, superficialValidation) 417 .ifPresent(moduleAnnotation -> builder.addAll(moduleAnnotation.includes())); 418 superclass = element.getSuperType(); 419 } 420 return builder.build(); 421 } 422 validateSubcomponentCreatorMethod()423 private void validateSubcomponentCreatorMethod() { 424 referencedSubcomponents.put(returnType.getTypeElement().getEnclosingTypeElement(), method); 425 426 if (!parameters.isEmpty()) { 427 report.addError(builderMethodRequiresNoArgs(), method); 428 } 429 430 XTypeElement creatorElement = returnType.getTypeElement(); 431 // TODO(sameb): The creator validator right now assumes the element is being compiled 432 // in this pass, which isn't true here. We should change error messages to spit out 433 // this method as the subject and add the original subject to the message output. 434 report.addSubreport(creatorValidator.validate(creatorElement)); 435 } 436 validateProvisionMethod()437 private void validateProvisionMethod() { 438 dependencyRequestValidator.validateDependencyRequest(report, method, returnType); 439 } 440 validateMembersInjectionMethod()441 private void validateMembersInjectionMethod() { 442 XType parameterType = getOnlyElement(parameterTypes); 443 report.addSubreport( 444 membersInjectionValidator.validateMembersInjectionMethod(method, parameterType)); 445 if (!(isVoid(returnType) || returnType.isSameType(parameterType))) { 446 report.addError( 447 "Members injection methods may only return the injected type or void.", method); 448 } 449 } 450 reportInvalidMethod()451 private void reportInvalidMethod() { 452 report.addError( 453 "This method isn't a valid provision method, members injection method or " 454 + "subcomponent factory method. Dagger cannot implement this method", 455 method); 456 } 457 } 458 validateNoConflictingEntryPoints()459 private void validateNoConflictingEntryPoints() { 460 // Collect entry point methods that are not overridden by others. If the "same" method is 461 // inherited from more than one supertype, each will be in the multimap. 462 SetMultimap<String, XMethodElement> entryPoints = HashMultimap.create(); 463 XTypeElements.getAllMethods(component).stream() 464 .filter(method -> isEntryPoint(method, method.asMemberOf(component.getType()))) 465 .forEach( 466 method -> addMethodUnlessOverridden(method, entryPoints.get(getSimpleName(method)))); 467 468 asMap(entryPoints).values().stream() 469 .filter(methods -> distinctKeys(methods).size() > 1) 470 .forEach(this::reportConflictingEntryPoints); 471 } 472 reportConflictingEntryPoints(Collection<XMethodElement> methods)473 private void reportConflictingEntryPoints(Collection<XMethodElement> methods) { 474 verify( 475 methods.stream().map(XMethodElement::getEnclosingElement).distinct().count() 476 == methods.size(), 477 "expected each method to be declared on a different type: %s", 478 methods); 479 StringBuilder message = new StringBuilder("Found conflicting entry point declarations. " 480 + "Getter methods on the component with the same name and signature must be for the " 481 + "same binding key since the generated component can only implement the method once. " 482 + "Found:"); 483 methodSignatureFormatter 484 .typedFormatter(component.getType()) 485 .formatIndentedList( 486 message, 487 ImmutableList.sortedCopyOf( 488 comparing(method -> method.getEnclosingElement().getClassName().canonicalName()), 489 methods), 490 1); 491 report.addError(message.toString()); 492 } 493 validateSubcomponentReferences()494 private void validateSubcomponentReferences() { 495 Maps.filterValues(referencedSubcomponents.asMap(), methods -> methods.size() > 1) 496 .forEach( 497 (subcomponent, methods) -> 498 report.addError( 499 String.format( 500 moreThanOneRefToSubcomponent(), 501 subcomponent.getQualifiedName(), 502 methods.stream() 503 .map(methodSignatureFormatter::formatWithoutReturnType) 504 .collect(toImmutableSet())), 505 component)); 506 } 507 validateComponentDependencies()508 private void validateComponentDependencies() { 509 for (XType type : componentAnnotation().dependencyTypes()) { 510 if (!isDeclared(type)) { 511 report.addError( 512 XTypes.toStableString(type) + " is not a valid component dependency type"); 513 } else if (type.getTypeElement().hasAnyAnnotation(moduleAnnotations())) { 514 report.addError( 515 XTypes.toStableString(type) + " is a module, which cannot be a component dependency"); 516 } 517 } 518 } 519 validateReferencedModules()520 private void validateReferencedModules() { 521 report.addSubreport( 522 moduleValidator.validateReferencedModules( 523 component, 524 componentAnnotation().annotation(), 525 componentKind().legalModuleKinds(), 526 new HashSet<>())); 527 } 528 validateSubcomponents()529 private void validateSubcomponents() { 530 // Make sure we validate any subcomponents we're referencing. 531 referencedSubcomponents 532 .keySet() 533 .forEach(subcomponent -> report.addSubreport(validate(subcomponent))); 534 } 535 distinctKeys(Set<XMethodElement> methods)536 private ImmutableSet<Key> distinctKeys(Set<XMethodElement> methods) { 537 return methods.stream() 538 .map(this::dependencyRequest) 539 .map(DependencyRequest::key) 540 .collect(toImmutableSet()); 541 } 542 dependencyRequest(XMethodElement method)543 private DependencyRequest dependencyRequest(XMethodElement method) { 544 XMethodType methodType = method.asMemberOf(component.getType()); 545 return componentKind().isProducer() 546 ? dependencyRequestFactory.forComponentProductionMethod(method, methodType) 547 : dependencyRequestFactory.forComponentProvisionMethod(method, methodType); 548 } 549 } 550 isEntryPoint(XMethodElement method, XMethodType methodType)551 private static boolean isEntryPoint(XMethodElement method, XMethodType methodType) { 552 return method.isAbstract() 553 && method.getParameters().isEmpty() 554 && !isVoid(methodType.getReturnType()) 555 && methodType.getTypeVariableNames().isEmpty(); 556 } 557 addMethodUnlessOverridden(XMethodElement method, Set<XMethodElement> methods)558 private void addMethodUnlessOverridden(XMethodElement method, Set<XMethodElement> methods) { 559 if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) { 560 methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod)); 561 methods.add(method); 562 } 563 } 564 565 /** 566 * Returns {@code true} if {@code overrider} overrides {@code overridden} considered from within 567 * the type that declares {@code overrider}. 568 */ 569 // TODO(dpb): Does this break for ECJ? overridesAsDeclared(XMethodElement overrider, XMethodElement overridden)570 private boolean overridesAsDeclared(XMethodElement overrider, XMethodElement overridden) { 571 return javacOverrides(overrider, overridden, asTypeElement(overrider.getEnclosingElement())); 572 } 573 checkForAnnotations(XType type, Set<ClassName> annotations)574 private static Optional<XAnnotation> checkForAnnotations(XType type, Set<ClassName> annotations) { 575 return Optional.ofNullable(type.getTypeElement()) 576 .flatMap(typeElement -> getAnyAnnotation(typeElement, annotations)); 577 } 578 } 579