Java: Add Annotation value convenience predicates

This commit is contained in:
Marcono1234
2021-07-10 22:38:38 +02:00
committed by Chris Smowton
parent 3165babc88
commit 536f5c7f89
11 changed files with 85 additions and 36 deletions

View File

@@ -50,6 +50,50 @@ class Annotation extends @annotation, Expr {
/** Gets the value of the annotation element with the specified `name`. */
Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) }
/**
* If the value type of the annotation element with the specified `name` is an enum type,
* gets the enum constant used as value for that element.
*/
EnumConstant getValueEnumConstant(string name) {
result = getValue(name).(FieldRead).getField()
}
/**
* If the value type of the annotation element with the specified `name` is `String`,
* gets the string value used for that element.
*/
string getValueString(string name) {
// Uses CompileTimeConstantExpr instead of StringLiteral because value can
// be read of final variable as well
result = getValue(name).(CompileTimeConstantExpr).getStringValue()
}
/**
* If the value type of the annotation element with the specified `name` is `int`,
* gets the int value used for that element.
*/
int getValueInt(string name) {
// Uses CompileTimeConstantExpr instead of IntegerLiteral because value can
// be read of final variable as well
result = getValue(name).(CompileTimeConstantExpr).getIntValue()
}
/**
* If the value type of the annotation element with the specified `name` is `boolean`,
* gets the boolean value used for that element.
*/
boolean getValueBoolean(string name) {
// Uses CompileTimeConstantExpr instead of BooleanLiteral because value can
// be read of final variable as well
result = getValue(name).(CompileTimeConstantExpr).getBooleanValue()
}
/**
* If the annotation element with the specified `name` has a Java `Class` as value type,
* gets the referenced type used as value for that element.
*/
Type getValueClass(string name) { result = getValue(name).(TypeLiteral).getReferencedType() }
/** Gets the element being annotated. */
Element getTarget() { result = this.getAnnotatedElement() }
@@ -66,10 +110,24 @@ class Annotation extends @annotation, Expr {
* be one of the elements of that array. Otherwise, the returned value will be the single
* expression defined for the value.
*/
Expr getAValue(string name) {
Expr getAValue(string name) { result = getAValue(name, _) }
/**
* Gets the value at a given index of the annotation element with the specified `name`, which must be
* declared as an array type.
*
* If the annotation element is defined with an array initializer, then the returned value will
* be the elements at the given index of that array. Otherwise, if the index is 0 the returned value
* will be the single expression defined for the value.
*/
Expr getAValue(string name, int index) {
this.getType().getAnnotationElement(name).getType() instanceof Array and
exists(Expr value | value = this.getValue(name) |
if value instanceof ArrayInit then result = value.(ArrayInit).getAnInit() else result = value
if value instanceof ArrayInit
then result = value.(ArrayInit).getInit(index)
else (
index = 0 and result = value
)
)
}

View File

@@ -18,14 +18,18 @@ class OverrideAnnotation extends Annotation {
class SuppressWarningsAnnotation extends Annotation {
SuppressWarningsAnnotation() { this.getType().hasQualifiedName("java.lang", "SuppressWarnings") }
/** Gets the `StringLiteral` of a warning suppressed by this annotation. */
StringLiteral getASuppressedWarningLiteral() {
result = this.getAValue() or
result = this.getAValue().(ArrayInit).getAnInit()
}
/**
* Gets the `StringLiteral` of a warning suppressed by this annotation. To get the name of a suppressed
* warning, prefer `getASuppressedWarning()`. That predicate considers more cases because it does not
* restrict results to `StringLiteral`.
*/
StringLiteral getASuppressedWarningLiteral() { result = this.getAValue(_) }
/** Gets the name of a warning suppressed by this annotation. */
string getASuppressedWarning() { result = this.getASuppressedWarningLiteral().getValue() }
string getASuppressedWarning() {
// Use CompileTimeConstantExpr because that covers more than StringLiteral result of getASuppressedWarningLiteral()
result = this.getAValue(_).(CompileTimeConstantExpr).getStringValue()
}
}
/** A `@Target` annotation. */
@@ -40,10 +44,7 @@ class TargetAnnotation extends Annotation {
*/
Expr getATargetExpression() {
not result instanceof ArrayInit and
(
result = this.getAValue() or
result = this.getAValue().(ArrayInit).getAnInit()
)
result = this.getAValue(_)
}
/**
@@ -54,7 +55,7 @@ class TargetAnnotation extends Annotation {
*/
string getATargetElementType() {
exists(EnumConstant ec |
ec = this.getATargetExpression().(VarAccess).getVariable() and
ec = this.getATargetExpression().(FieldRead).getField() and
ec.getDeclaringType().hasQualifiedName("java.lang.annotation", "ElementType")
|
result = ec.getName()
@@ -82,7 +83,7 @@ class RetentionAnnotation extends Annotation {
*/
string getRetentionPolicy() {
exists(EnumConstant ec |
ec = this.getRetentionPolicyExpression().(VarAccess).getVariable() and
ec = this.getRetentionPolicyExpression().(FieldRead).getField() and
ec.getDeclaringType().hasQualifiedName("java.lang.annotation", "RetentionPolicy")
|
result = ec.getName()

View File

@@ -161,15 +161,13 @@ class TestNGTestMethod extends Method {
exists(TestNGTestAnnotation testAnnotation |
testAnnotation = this.getAnAnnotation() and
// The data provider must have the same name as the referenced data provider
result.getDataProviderName() =
testAnnotation.getValue("dataProvider").(StringLiteral).getValue()
result.getDataProviderName() = testAnnotation.getValueString("dataProvider")
|
// Either the data provider should be on the current class, or a supertype
this.getDeclaringType().getAnAncestor() = result.getDeclaringType()
or
// Or the data provider class should be declared
result.getDeclaringType() =
testAnnotation.getValue("dataProviderClass").(TypeLiteral).getReferencedType()
result.getDeclaringType() = testAnnotation.getValueClass("dataProviderClass")
)
}
}

View File

@@ -60,7 +60,7 @@ class JaxbType extends Class {
this.getAnAnnotation() = a and
a.getType().(JaxbAnnotationType).hasName("XmlAccessorType")
|
result.getAnAccess() = a.getValue("value")
result = a.getValueEnumConstant("value")
)
}

View File

@@ -64,5 +64,5 @@ class RunWithAnnotation extends Annotation {
/**
* Gets the runner that will be used.
*/
Type getRunner() { result = this.getValue("value").(TypeLiteral).getReferencedType() }
Type getRunner() { result = this.getValueClass("value") }
}

View File

@@ -37,8 +37,7 @@ class PersistentEntity extends RefType {
*/
string getAccessTypeFromAnnotation() {
exists(AccessAnnotation accessType | accessType = this.getAnAnnotation() |
result =
accessType.getValue("value").(FieldRead).getField().(EnumConstant).getName().toLowerCase()
result = accessType.getValueEnumConstant("value").getName().toLowerCase()
)
}
}

View File

@@ -311,9 +311,7 @@ class SpringQualifierDefinitionAnnotation extends Annotation {
/**
* Gets the value of the qualifier field for this qualifier.
*/
string getQualifierValue() {
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue()
}
string getQualifierValue() { result = this.getValueString("value") }
}
/**
@@ -325,9 +323,7 @@ class SpringQualifierAnnotation extends Annotation {
/**
* Gets the value of the qualifier field for this qualifier.
*/
string getQualifierValue() {
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue()
}
string getQualifierValue() { result = getValueString("value") }
/**
* Gets the bean definition in an XML file that this qualifier resolves to, if any.
@@ -350,9 +346,7 @@ class SpringResourceAnnotation extends Annotation {
/**
* Gets the specified name value, if any.
*/
string getNameValue() {
result = this.getValue("name").(CompileTimeConstantExpr).getStringValue()
}
string getNameValue() { result = getValueString("name") }
/**
* Gets the bean definition in an XML file that the resource resolves to, if any.

View File

@@ -144,8 +144,7 @@ class SpringComponent extends RefType {
if exists(this.getComponentAnnotation().getValue("value"))
then
// If the name has been specified in the component annotation, use that.
result =
this.getComponentAnnotation().getValue("value").(CompileTimeConstantExpr).getStringValue()
result = getComponentAnnotation().getValueString("value")
else
// Otherwise use the name of the class, with the initial letter lower cased.
exists(string name | name = this.getName() |

View File

@@ -155,7 +155,7 @@ class SpringRequestMappingMethod extends SpringControllerMethod {
/** Gets the "value" @RequestMapping annotation value, if present. */
string getValue() {
result = requestMappingAnnotation.getValue("value").(CompileTimeConstantExpr).getStringValue()
result = requestMappingAnnotation.getValueString("value")
}
/** Holds if this is considered an `@ResponseBody` method. */

View File

@@ -23,7 +23,7 @@ class SuppressionAnnotation extends SuppressWarningsAnnotation {
string text;
SuppressionAnnotation() {
text = this.getASuppressedWarningLiteral().getValue() and
text = this.getASuppressedWarning() and
exists(getAnnotationText(text))
}

View File

@@ -47,7 +47,7 @@ class SpringControllerRequestMappingGetMethod extends SpringControllerGetMethod
.getType()
.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
(
this.getAnAnnotation().getValue("method").(VarAccess).getVariable().getName() = "GET" or
this.getAnAnnotation().getValueEnumConstant("method").getName() = "GET" or
this.getAnAnnotation().getValue("method").(ArrayInit).getSize() = 0 //Java code example: @RequestMapping(value = "test")
) and
not this.getAParamType().getName() = "MultipartFile"