From 02c8fe9346be7b5b94bd7d5dda355320fcbf2366 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sat, 10 Jul 2021 23:14:31 +0200 Subject: [PATCH] Java: Add convenience predicates for `AnnotationType` --- java/ql/lib/semmle/code/java/Annotation.qll | 41 +++++++++++++++++-- .../Reflection/AnnotationPresentCheck.ql | 5 +-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Annotation.qll b/java/ql/lib/semmle/code/java/Annotation.qll index f4419406e45..5852c27a95f 100644 --- a/java/ql/lib/semmle/code/java/Annotation.qll +++ b/java/ql/lib/semmle/code/java/Annotation.qll @@ -227,10 +227,43 @@ class AnnotationType extends Interface { /** Holds if this annotation type is annotated with the meta-annotation `@Inherited`. */ predicate isInherited() { - exists(Annotation ann | - ann.getAnnotatedElement() = this and - ann.getType().hasQualifiedName("java.lang.annotation", "Inherited") - ) + getADeclaredAnnotation().getType().hasQualifiedName("java.lang.annotation", "Inherited") + } + + /** Holds if this annotation type is annotated with the meta-annotation `@Documented`. */ + predicate isDocumented() { + getADeclaredAnnotation().getType().hasQualifiedName("java.lang.annotation", "Documented") + } + + /** + * Gets the retention policy of this annotation type, that is, the name of one of the + * enum constants of `java.lang.annotation.RetentionPolicy`. If no explicit retention + * policy is specified the result is `CLASS`. + */ + string getRetentionPolicy() { + if getADeclaredAnnotation() instanceof RetentionAnnotation + then result = getADeclaredAnnotation().(RetentionAnnotation).getRetentionPolicy() + else + // If not explicitly specified retention is CLASS + result = "CLASS" + } + + /** + * Holds if the element type is a possible target for this annotation type. + * The `elementType` is the name of one of the `java.lang.annotation.ElementType` + * enum constants. If no explicit target is specified for this annotation type + * it is considered to be applicable to all elements. + */ + // Note: Cannot use a predicate with string as result because annotation type without + // explicit @Target can be applied to all targets, requiring to hardcode element types here + bindingset[elementType] + predicate isATargetType(string elementType) { + if getADeclaredAnnotation() instanceof TargetAnnotation + then elementType = getADeclaredAnnotation().(TargetAnnotation).getATargetElementType() + else + // No Target annotation means "applicable to all contexts" since JDK 14, see https://bugs.openjdk.java.net/browse/JDK-8231435 + // The compiler does not completely implement that, but pretend it did + any() } } diff --git a/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql b/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql index 22729eebd66..2bffa1ea4f4 100644 --- a/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql +++ b/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql @@ -19,8 +19,5 @@ where m.getNumberOfParameters() = 1 and c.getArgument(0).getType() = p and p.getATypeArgument() = t and - not exists(RetentionAnnotation a | - t.getAnAnnotation() = a and - a.getAValue().(VarAccess).getVariable().hasName("RUNTIME") - ) + t.getRetentionPolicy() != "RUNTIME" select c, "Call to isAnnotationPresent where no annotation has the RUNTIME retention policy."