• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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