Java: Add classes and predicates for @Repeatable

This commit is contained in:
Marcono1234
2021-07-10 23:46:27 +02:00
committed by Chris Smowton
parent 02c8fe9346
commit c226758889
2 changed files with 60 additions and 0 deletions

View File

@@ -184,6 +184,8 @@ class Annotatable extends Element {
/**
* Gets an annotation that applies to this element, including inherited annotations.
* The results only include _direct_ annotations; _indirect_ annotations, that is
* repeated annotations in an (implicit) container annotation, are not included.
*/
// This predicate is overridden by Class to consider inherited annotations
cached
@@ -194,6 +196,42 @@ class Annotatable extends Element {
*/
Annotation getADeclaredAnnotation() { result.getAnnotatedElement() = this }
/** Gets an _indirect_ (= repeated) annotation. */
// 'indirect' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
private Annotation getAnIndirectAnnotation() {
exists(AnnotationType t, Annotation containerAnn |
t = result.getType() and
containerAnn = getADeclaredAnnotation() and
containerAnn.getType() = t.getContainingAnnotationType()
|
result = containerAnn.getAValue("value")
)
}
private Annotation getAnAssociatedAnnotation(AnnotationType t) {
result.getType() = t and
// Direct or indirect annotation
if getADeclaredAnnotation().getType() = t or getAnIndirectAnnotation().getType() = t
then (
result = getADeclaredAnnotation() or result = getAnIndirectAnnotation()
) else (
// Only if neither a direct nor an indirect annotation is present look for an inherited one
t.isInherited() and
// @Inherited only works for classes; cast to Annotatable is necessary because predicate is private
result = this.(Class).getASupertype().(Class).(Annotatable).getAnAssociatedAnnotation(t)
)
}
/**
* Gets an annotation _associated_ with this element, that is:
* - An annotation directly present on this element, or
* - An annotation indirectly present on this element (in the form of a repeated annotation), or
* - If an annotation of a type is neither directly nor indirectly present
* the result is an associated inherited annotation (recursively)
*/
// 'associated' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
Annotation getAnAssociatedAnnotation() { result = getAnAssociatedAnnotation(_) }
/**
* Holds if this or any enclosing `Annotatable` has a `@SuppressWarnings("<category>")`
* annotation attached to it for the specified `category`.
@@ -265,6 +303,17 @@ class AnnotationType extends Interface {
// The compiler does not completely implement that, but pretend it did
any()
}
/** Holds if this annotation type is annotated with the meta-annotation `@Repeatable`. */
predicate isRepeatable() { getADeclaredAnnotation() instanceof RepeatableAnnotation }
/**
* If this annotation type is annotated with the meta-annotation `@Repeatable`,
* gets the annotation type which acts as _containing annotation type_.
*/
AnnotationType getContainingAnnotationType() {
result = getADeclaredAnnotation().(RepeatableAnnotation).getContainingType()
}
}
/** An annotation element is a member declared in an annotation type. */

View File

@@ -91,6 +91,17 @@ class RetentionAnnotation extends Annotation {
}
}
/** A `@Repeatable` annotation. */
class RepeatableAnnotation extends Annotation {
RepeatableAnnotation() { getType().hasQualifiedName("java.lang.annotation", "Repeatable") }
/**
* Gets the annotation type which acts as _containing type_, grouping multiple
* repeatable annotations together.
*/
AnnotationType getContainingType() { result = getValueClass("value") }
}
/**
* An annotation suggesting that the annotated element may be accessed reflectively.
*