Skip to content

Commit 998aa95

Browse files
Marcono1234smowton
authored andcommitted
Java: Add convenience array value Annotation predicates
1 parent 47e3895 commit 998aa95

File tree

8 files changed

+71
-38
lines changed

8 files changed

+71
-38
lines changed

java/ql/lib/semmle/code/java/Annotation.qll

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,35 @@ class Annotation extends @annotation, Expr {
4646

4747
/**
4848
* Gets a value of an annotation element. This includes default values in case
49-
* no explicit value is specified.
49+
* no explicit value is specified. For elements with an array value type this
50+
* might have an `ArrayInit` as result. To properly handle array values, prefer
51+
* the predicate `getAnArrayValue`.
5052
*/
5153
Expr getAValue() { filteredAnnotValue(this, _, result) }
5254

5355
/**
5456
* Gets the value of the annotation element with the specified `name`.
5557
* This includes default values in case no explicit value is specified.
58+
* For elements with an array value type this might have an `ArrayInit` as result.
59+
* To properly handle array values, prefer the predicate `getAnArrayValue`.
5660
*/
5761
Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) }
5862

5963
/**
6064
* If the value type of the annotation element with the specified `name` is an enum type,
6165
* gets the enum constant used as value for that element. This includes default values in
6266
* case no explicit value is specified.
67+
*
68+
* If the element value type is an enum type array, use `getAnEnumConstantArrayValue`.
6369
*/
6470
EnumConstant getEnumConstantValue(string name) { result = getValue(name).(FieldRead).getField() }
6571

6672
/**
6773
* If the value type of the annotation element with the specified `name` is `String`,
6874
* gets the string value used for that element. This includes default values in case no
6975
* explicit value is specified.
76+
*
77+
* If the element value type is a string array, use `getAStringArrayValue`.
7078
*/
7179
string getStringValue(string name) {
7280
// Uses CompileTimeConstantExpr instead of StringLiteral because value can
@@ -78,6 +86,8 @@ class Annotation extends @annotation, Expr {
7886
* If the value type of the annotation element with the specified `name` is `int`,
7987
* gets the int value used for that element. This includes default values in case no
8088
* explicit value is specified.
89+
*
90+
* If the element value type is an `int` array, use `getAnIntArrayValue`.
8191
*/
8292
int getIntValue(string name) {
8393
// Uses CompileTimeConstantExpr instead of IntegerLiteral because value can
@@ -100,6 +110,8 @@ class Annotation extends @annotation, Expr {
100110
* If the annotation element with the specified `name` has a Java `Class` as value type,
101111
* gets the referenced type used as value for that element. This includes default values
102112
* in case no explicit value is specified.
113+
*
114+
* If the element value type is a `Class` array, use `getATypeArrayValue`.
103115
*/
104116
Type getTypeValue(string name) { result = getValue(name).(TypeLiteral).getReferencedType() }
105117

@@ -125,6 +137,50 @@ class Annotation extends @annotation, Expr {
125137
*/
126138
deprecated Expr getAValue(string name) { result = getAnArrayValue(name) }
127139

140+
/**
141+
* Gets a value of the annotation element with the specified `name`, which must be declared as an enum
142+
* type array. This includes default values in case no explicit value is specified.
143+
*
144+
* If the annotation element is defined with an array initializer, then the result will be one of the
145+
* elements of that array. Otherwise, the result will be the single expression defined for the value.
146+
*/
147+
EnumConstant getAnEnumConstantArrayValue(string name) {
148+
result = this.getAnArrayValue(name).(FieldRead).getField()
149+
}
150+
151+
/**
152+
* Gets a value of the annotation element with the specified `name`, which must be declared as a string
153+
* array. This includes default values in case no explicit value is specified.
154+
*
155+
* If the annotation element is defined with an array initializer, then the result will be one of the
156+
* elements of that array. Otherwise, the result will be the single expression defined for the value.
157+
*/
158+
string getAStringArrayValue(string name) {
159+
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getStringValue()
160+
}
161+
162+
/**
163+
* Gets a value of the annotation element with the specified `name`, which must be declared as an `int`
164+
* array. This includes default values in case no explicit value is specified.
165+
*
166+
* If the annotation element is defined with an array initializer, then the result will be one of the
167+
* elements of that array. Otherwise, the result will be the single expression defined for the value.
168+
*/
169+
int getAnIntArrayValue(string name) {
170+
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getIntValue()
171+
}
172+
173+
/**
174+
* Gets a value of the annotation element with the specified `name`, which must be declared as a `Class`
175+
* array. This includes default values in case no explicit value is specified.
176+
*
177+
* If the annotation element is defined with an array initializer, then the result will be one of the
178+
* elements of that array. Otherwise, the result will be the single expression defined for the value.
179+
*/
180+
Type getATypeArrayValue(string name) {
181+
result = this.getAnArrayValue(name).(TypeLiteral).getReferencedType()
182+
}
183+
128184
/**
129185
* Gets the value at a given index of the annotation element with the specified `name`, which must be
130186
* declared as an array type. This includes default values in case no explicit value is specified.

java/ql/lib/semmle/code/java/Dependency.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ predicate depends(RefType t, RefType dep) {
7171
a.getAnnotatedElement().(Member).getDeclaringType() = t
7272
|
7373
usesType(a.getType(), dep) or
74-
usesType(a.getAValue().getType(), dep)
74+
usesType(a.getAValue().getType(), dep) or
75+
usesType(a.getAnArrayValue(_).getType(), dep)
7576
)
7677
or
7778
// the type accessed in an `instanceof` expression in `t`.

java/ql/lib/semmle/code/java/DependencyCounts.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ predicate numDepends(RefType t, RefType dep, int value) {
9090
|
9191
elem = a and usesType(a.getType(), dep)
9292
or
93-
elem = a.getAValue() and
93+
elem = [a.getAValue(), a.getAnArrayValue(_)] and
9494
elem.getFile().getExtension() = "java" and
9595
usesType(elem.(Expr).getType(), dep)
9696
)

java/ql/lib/semmle/code/java/JDKAnnotations.qll

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class SuppressWarningsAnnotation extends Annotation {
2727

2828
/** Gets the name of a warning suppressed by this annotation. */
2929
string getASuppressedWarning() {
30-
// Use CompileTimeConstantExpr because that covers more than StringLiteral result of getASuppressedWarningLiteral()
31-
result = this.getAnArrayValue("value").(CompileTimeConstantExpr).getStringValue()
30+
// Don't use getASuppressedWarningLiteral() because that restricts results to StringLiteral
31+
result = this.getAStringArrayValue("value")
3232
}
3333
}
3434

@@ -42,25 +42,15 @@ class TargetAnnotation extends Annotation {
4242
* For example, the field access `ElementType.FIELD` is a target expression in
4343
* `@Target({ElementType.FIELD, ElementType.METHOD})`.
4444
*/
45-
Expr getATargetExpression() {
46-
not result instanceof ArrayInit and
47-
result = this.getAnArrayValue("value")
48-
}
45+
Expr getATargetExpression() { result = this.getAnArrayValue("value") }
4946

5047
/**
5148
* Gets the name of a target element type.
5249
*
5350
* For example, `METHOD` is the name of a target element type in
5451
* `@Target({ElementType.FIELD, ElementType.METHOD})`.
5552
*/
56-
string getATargetElementType() {
57-
exists(EnumConstant ec |
58-
ec = this.getATargetExpression().(FieldRead).getField() and
59-
ec.getDeclaringType().hasQualifiedName("java.lang.annotation", "ElementType")
60-
|
61-
result = ec.getName()
62-
)
63-
}
53+
string getATargetElementType() { result = this.getAnEnumConstantArrayValue("value").getName() }
6454
}
6555

6656
/** A `@Retention` annotation. */

java/ql/lib/semmle/code/java/UnitTests.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,7 @@ class TestNGListenersAnnotation extends TestNGAnnotation {
225225
/**
226226
* Gets a listener defined in this annotation.
227227
*/
228-
TestNGListenerImpl getAListener() {
229-
result = this.getAnArrayValue("value").(TypeLiteral).getReferencedType()
230-
}
228+
TestNGListenerImpl getAListener() { result = this.getATypeArrayValue("value") }
231229
}
232230

233231
/**

java/ql/lib/semmle/code/java/frameworks/JaxWS.qll

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,7 @@ class JaxRSProducesAnnotation extends JaxRSAnnotation {
296296
/**
297297
* Gets a declared content type that can be produced by this resource.
298298
*/
299-
Expr getADeclaredContentTypeExpr() {
300-
result = this.getAValue() and not result instanceof ArrayInit
301-
or
302-
result = this.getAValue().(ArrayInit).getAnInit()
303-
}
299+
Expr getADeclaredContentTypeExpr() { result = this.getAnArrayValue("value") }
304300
}
305301

306302
/** An `@Consumes` annotation that describes content types can be consumed by this resource. */

java/ql/lib/semmle/code/java/frameworks/MyBatis.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ class IbatisSqlOperationAnnotation extends Annotation {
8585
/**
8686
* Gets this annotation's SQL statement string.
8787
*/
88-
string getSqlValue() {
89-
result = this.getAnArrayValue("value").(CompileTimeConstantExpr).getStringValue()
90-
}
88+
string getSqlValue() { result = this.getAStringArrayValue("value") }
9189
}
9290

9391
/**

java/ql/lib/semmle/code/java/frameworks/spring/SpringComponentScan.qll

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,10 @@ class SpringComponentScan extends Annotation {
4040
*/
4141
string getBasePackages() {
4242
// "value" and "basePackages" are synonymous, and are simple strings
43-
result = this.getAnArrayValue("basePackages").(StringLiteral).getValue()
43+
result = this.getAStringArrayValue(["basePackages", "value"])
4444
or
45-
result = this.getAnArrayValue("value").(StringLiteral).getValue()
46-
or
47-
exists(TypeLiteral typeLiteral |
48-
// Base package classes are type literals whose package should be considered a base package.
49-
typeLiteral = this.getAnArrayValue("basePackageClasses")
50-
|
51-
result = typeLiteral.getReferencedType().(RefType).getPackage().getName()
52-
)
45+
// Base package classes are type literals whose package should be considered a base package.
46+
result = this.getATypeArrayValue("basePackageClasses").(RefType).getPackage().getName()
5347
}
5448
}
5549

@@ -203,7 +197,7 @@ class SpringComponent extends RefType {
203197
.getType()
204198
.hasQualifiedName("org.springframework.context.annotation", "Profile")
205199
|
206-
result = profileAnnotation.getAnArrayValue("value").(StringLiteral).getValue()
200+
result = profileAnnotation.getAStringArrayValue("value")
207201
)
208202
}
209203
}

0 commit comments

Comments
 (0)