@@ -184,6 +184,8 @@ class Annotatable extends Element {
184184
185185 /**
186186 * Gets an annotation that applies to this element, including inherited annotations.
187+ * The results only include _direct_ annotations; _indirect_ annotations, that is
188+ * repeated annotations in an (implicit) container annotation, are not included.
187189 */
188190 // This predicate is overridden by Class to consider inherited annotations
189191 cached
@@ -194,6 +196,42 @@ class Annotatable extends Element {
194196 */
195197 Annotation getADeclaredAnnotation ( ) { result .getAnnotatedElement ( ) = this }
196198
199+ /** Gets an _indirect_ (= repeated) annotation. */
200+ // 'indirect' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
201+ private Annotation getAnIndirectAnnotation ( ) {
202+ exists ( AnnotationType t , Annotation containerAnn |
203+ t = result .getType ( ) and
204+ containerAnn = getADeclaredAnnotation ( ) and
205+ containerAnn .getType ( ) = t .getContainingAnnotationType ( )
206+ |
207+ result = containerAnn .getAValue ( "value" )
208+ )
209+ }
210+
211+ private Annotation getAnAssociatedAnnotation ( AnnotationType t ) {
212+ result .getType ( ) = t and
213+ // Direct or indirect annotation
214+ if getADeclaredAnnotation ( ) .getType ( ) = t or getAnIndirectAnnotation ( ) .getType ( ) = t
215+ then (
216+ result = getADeclaredAnnotation ( ) or result = getAnIndirectAnnotation ( )
217+ ) else (
218+ // Only if neither a direct nor an indirect annotation is present look for an inherited one
219+ t .isInherited ( ) and
220+ // @Inherited only works for classes; cast to Annotatable is necessary because predicate is private
221+ result = this .( Class ) .getASupertype ( ) .( Class ) .( Annotatable ) .getAnAssociatedAnnotation ( t )
222+ )
223+ }
224+
225+ /**
226+ * Gets an annotation _associated_ with this element, that is:
227+ * - An annotation directly present on this element, or
228+ * - An annotation indirectly present on this element (in the form of a repeated annotation), or
229+ * - If an annotation of a type is neither directly nor indirectly present
230+ * the result is an associated inherited annotation (recursively)
231+ */
232+ // 'associated' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
233+ Annotation getAnAssociatedAnnotation ( ) { result = getAnAssociatedAnnotation ( _) }
234+
197235 /**
198236 * Holds if this or any enclosing `Annotatable` has a `@SuppressWarnings("<category>")`
199237 * annotation attached to it for the specified `category`.
@@ -265,6 +303,17 @@ class AnnotationType extends Interface {
265303 // The compiler does not completely implement that, but pretend it did
266304 any ( )
267305 }
306+
307+ /** Holds if this annotation type is annotated with the meta-annotation `@Repeatable`. */
308+ predicate isRepeatable ( ) { getADeclaredAnnotation ( ) instanceof RepeatableAnnotation }
309+
310+ /**
311+ * If this annotation type is annotated with the meta-annotation `@Repeatable`,
312+ * gets the annotation type which acts as _containing annotation type_.
313+ */
314+ AnnotationType getContainingAnnotationType ( ) {
315+ result = getADeclaredAnnotation ( ) .( RepeatableAnnotation ) .getContainingType ( )
316+ }
268317}
269318
270319/** An annotation element is a member declared in an annotation type. */
0 commit comments