@@ -184,6 +184,8 @@ class Annotatable extends Element {
184
184
185
185
/**
186
186
* 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.
187
189
*/
188
190
// This predicate is overridden by Class to consider inherited annotations
189
191
cached
@@ -194,6 +196,42 @@ class Annotatable extends Element {
194
196
*/
195
197
Annotation getADeclaredAnnotation ( ) { result .getAnnotatedElement ( ) = this }
196
198
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
+
197
235
/**
198
236
* Holds if this or any enclosing `Annotatable` has a `@SuppressWarnings("<category>")`
199
237
* annotation attached to it for the specified `category`.
@@ -265,6 +303,17 @@ class AnnotationType extends Interface {
265
303
// The compiler does not completely implement that, but pretend it did
266
304
any ( )
267
305
}
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
+ }
268
317
}
269
318
270
319
/** An annotation element is a member declared in an annotation type. */
0 commit comments