Skip to content

Commit 5a12f64

Browse files
committed
Java: Add annotation tests
1 parent 70d1058 commit 5a12f64

11 files changed

+867
-17
lines changed

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

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ class Annotation extends @annotation, Expr {
6666
*/
6767
Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) }
6868

69+
/**
70+
* Gets the value of the annotation element, if its type is not an array.
71+
* This guarantees that for consistency even elements of type array with a
72+
* single value have no result, to prevent accidental error-prone usage.
73+
*/
74+
private Expr getNonArrayValue(string name) {
75+
result = this.getValue(name) and
76+
not this.getAnnotationElement(name).getType() instanceof Array
77+
}
78+
6979
/**
7080
* If the value type of the annotation element with the specified `name` is an enum type,
7181
* gets the enum constant used as value for that element. This includes default values in
@@ -74,7 +84,7 @@ class Annotation extends @annotation, Expr {
7484
* If the element value type is an enum type array, use `getAnEnumConstantArrayValue`.
7585
*/
7686
EnumConstant getEnumConstantValue(string name) {
77-
result = this.getValue(name).(FieldRead).getField()
87+
result = this.getNonArrayValue(name).(FieldRead).getField()
7888
}
7989

8090
/**
@@ -87,20 +97,23 @@ class Annotation extends @annotation, Expr {
8797
string getStringValue(string name) {
8898
// Uses CompileTimeConstantExpr instead of StringLiteral because this can for example
8999
// be a read from a final variable as well.
90-
result = this.getValue(name).(CompileTimeConstantExpr).getStringValue()
100+
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getStringValue()
91101
}
92102

93103
/**
94-
* If the value type of the annotation element with the specified `name` is `int`,
95-
* gets the int value used for that element. This includes default values in case no
96-
* explicit value is specified.
104+
* If the value type of the annotation element with the specified `name` is `int` or
105+
* a smaller integral type or `char`, gets the int value used for that element.
106+
* This includes default values in case no explicit value is specified.
97107
*
98-
* If the element value type is an `int` array, use `getAnIntArrayValue`.
108+
* If the element value type is an `int` array or an array of a smaller integral
109+
* type or `char`, use `getAnIntArrayValue`.
99110
*/
100111
int getIntValue(string name) {
101112
// Uses CompileTimeConstantExpr instead of IntegerLiteral because this can for example
102113
// be a read from a final variable as well.
103-
result = this.getValue(name).(CompileTimeConstantExpr).getIntValue()
114+
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getIntValue() and
115+
// Verify that type is integral; ignore floating point elements with IntegerLiteral as value
116+
this.getAnnotationElement(name).getType().hasName(["byte", "short", "int", "char"])
104117
}
105118

106119
/**
@@ -111,7 +124,7 @@ class Annotation extends @annotation, Expr {
111124
boolean getBooleanValue(string name) {
112125
// Uses CompileTimeConstantExpr instead of BooleanLiteral because this can for example
113126
// be a read from a final variable as well.
114-
result = this.getValue(name).(CompileTimeConstantExpr).getBooleanValue()
127+
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getBooleanValue()
115128
}
116129

117130
/**
@@ -121,7 +134,9 @@ class Annotation extends @annotation, Expr {
121134
*
122135
* If the element value type is a `Class` array, use `getATypeArrayValue`.
123136
*/
124-
Type getTypeValue(string name) { result = this.getValue(name).(TypeLiteral).getReferencedType() }
137+
Type getTypeValue(string name) {
138+
result = this.getNonArrayValue(name).(TypeLiteral).getReferencedType()
139+
}
125140

126141
/** Gets the element being annotated. */
127142
Element getTarget() { result = this.getAnnotatedElement() }
@@ -169,13 +184,16 @@ class Annotation extends @annotation, Expr {
169184

170185
/**
171186
* Gets a value of the annotation element with the specified `name`, which must be declared as an `int`
172-
* array. This includes default values in case no explicit value is specified.
187+
* array or an array of a smaller integral type or `char`. This includes default values in case no
188+
* explicit value is specified.
173189
*
174190
* If the annotation element is defined with an array initializer, then the result will be one of the
175191
* elements of that array. Otherwise, the result will be the single expression used as value.
176192
*/
177193
int getAnIntArrayValue(string name) {
178-
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getIntValue()
194+
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getIntValue() and
195+
// Verify that type is integral; ignore floating point elements with IntegerLiteral as value
196+
this.getAnnotationElement(name).getType().hasName(["byte[]", "short[]", "int[]", "char[]"])
179197
}
180198

181199
/**
@@ -194,14 +212,16 @@ class Annotation extends @annotation, Expr {
194212
* declared as an array type. This includes default values in case no explicit value is specified.
195213
*
196214
* If the annotation element is defined with an array initializer, then the result will be the element
197-
* at the given index of that array. Otherwise, the result will be the single expression defined for
198-
* the value and the `index` will be 0.
215+
* at the given index of that array, starting at 0. Otherwise, the result will be the single expression
216+
* defined for the value and the `index` will be 0.
199217
*/
200218
Expr getArrayValue(string name, int index) {
201219
this.getType().getAnnotationElement(name).getType() instanceof Array and
202220
exists(Expr value | value = this.getValue(name) |
203221
if value instanceof ArrayInit
204-
then result = value.(ArrayInit).getInit(index)
222+
then
223+
// TODO: Currently reports incorrect index values in some cases, see https://github.com/github/codeql/issues/8645
224+
result = value.(ArrayInit).getInit(index)
205225
else (
206226
index = 0 and result = value
207227
)
@@ -234,15 +254,21 @@ private predicate sourceAnnotValue(Annotation a, Method m, Expr val) {
234254

235255
/** An abstract representation of language elements that can be annotated. */
236256
class Annotatable extends Element {
237-
/** Holds if this element has an annotation, including inherited annotations. */
257+
/**
258+
* Holds if this element has an annotation, including inherited annotations.
259+
* The retention policy of the annotation type is not considered.
260+
*/
238261
predicate hasAnnotation() { exists(this.getAnAnnotation()) }
239262

240-
/** Holds if this element has a declared annotation, excluding inherited annotations. */
263+
/**
264+
* Holds if this element has a declared annotation, excluding inherited annotations.
265+
* The retention policy of the annotation type is not considered.
266+
*/
241267
predicate hasDeclaredAnnotation() { exists(this.getADeclaredAnnotation()) }
242268

243269
/**
244270
* Holds if this element has the specified annotation, including inherited
245-
* annotations.
271+
* annotations. The retention policy of the annotation type is not considered.
246272
*/
247273
predicate hasAnnotation(string package, string name) {
248274
exists(AnnotationType at | at = this.getAnAnnotation().getType() |
@@ -254,6 +280,7 @@ class Annotatable extends Element {
254280
* Gets an annotation that applies to this element, including inherited annotations.
255281
* The results only include _direct_ annotations; _indirect_ annotations, that is
256282
* repeated annotations in an (implicit) container annotation, are not included.
283+
* The retention policy of the annotation type is not considered.
257284
*/
258285
cached
259286
Annotation getAnAnnotation() {
@@ -263,6 +290,7 @@ class Annotatable extends Element {
263290

264291
/**
265292
* Gets an annotation that is declared on this element, excluding inherited annotations.
293+
* The retention policy of the annotation type is not considered.
266294
*/
267295
Annotation getADeclaredAnnotation() { result.getAnnotatedElement() = this }
268296

@@ -302,6 +330,8 @@ class Annotatable extends Element {
302330
* - An annotation indirectly present on this element (in the form of a repeated annotation), or
303331
* - If an annotation of a type is neither directly nor indirectly present
304332
* the result is an associated inherited annotation (recursively)
333+
*
334+
* The retention policy of the annotation type is not considered.
305335
*/
306336
Annotation getAnAssociatedAnnotation() { result = this.getAnAssociatedAnnotation(_) }
307337

@@ -320,6 +350,11 @@ class Annotatable extends Element {
320350
or
321351
this.(NestedClass).getEnclosingType().suppressesWarningsAbout(category)
322352
or
353+
this.(LocalClassOrInterface)
354+
.getLocalTypeDeclStmt()
355+
.getEnclosingCallable()
356+
.suppressesWarningsAbout(category)
357+
or
323358
this.(LocalVariableDecl).getCallable().suppressesWarningsAbout(category)
324359
}
325360
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
declaredAnnotation
2+
| Annotatable.java:8:16:8:40 | CustomInheritedAnnotation | Annotatable.java:7:5:7:14 | Inherited |
3+
| Annotatable.java:14:11:14:22 | WithDeclared | Annotatable.java:12:5:12:21 | CustomAnnotation |
4+
| Annotatable.java:14:11:14:22 | WithDeclared | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
5+
| Annotatable.java:17:14:17:31 | methodWithDeclared | Annotatable.java:16:9:16:49 | CustomInheritedAnnotation |
6+
| Annotatable.java:22:14:22:31 | methodWithDeclared | Annotatable.java:21:9:21:17 | Override |
7+
| Annotatable.java:30:11:30:41 | SubclassDeclaringSameAnnotation | Annotatable.java:29:5:29:37 | CustomInheritedAnnotation |
8+
| Annotatable.java:35:15:35:35 | InterfaceWithDeclared | Annotatable.java:34:5:34:38 | CustomInheritedAnnotation |
9+
| Annotatable.java:47:16:47:35 | RepeatableAnnotation | Annotatable.java:46:5:46:42 | Repeatable |
10+
| Annotatable.java:52:16:52:43 | InheritedContainerAnnotation | Annotatable.java:51:5:51:14 | Inherited |
11+
| Annotatable.java:58:16:58:44 | InheritedRepeatableAnnotation | Annotatable.java:56:5:56:14 | Inherited |
12+
| Annotatable.java:58:16:58:44 | InheritedRepeatableAnnotation | Annotatable.java:57:5:57:51 | Repeatable |
13+
| Annotatable.java:63:16:63:44 | InheritedContainerAnnotation2 | Annotatable.java:62:5:62:14 | Inherited |
14+
| Annotatable.java:71:16:71:47 | NonInheritedRepeatableAnnotation | Annotatable.java:70:5:70:52 | Repeatable |
15+
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:75:5:75:33 | RepeatableAnnotation |
16+
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
17+
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:77:5:77:45 | NonInheritedRepeatableAnnotation |
18+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | ContainerAnnotation |
19+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
20+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
21+
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:90:5:90:37 | InheritedContainerAnnotation |
22+
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:91:5:91:38 | InheritedContainerAnnotation2 |
23+
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
24+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | ContainerAnnotation |
25+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
26+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
27+
| Annotatable.java:116:14:116:39 | methodWithAssociatedSingle | Annotatable.java:115:9:115:53 | InheritedRepeatableAnnotation |
28+
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
29+
| Annotatable.java:126:14:126:39 | methodWithAssociatedSingle | Annotatable.java:125:9:125:17 | Override |
30+
| Annotatable.java:129:14:129:33 | methodWithAssociated | Annotatable.java:128:9:128:17 | Override |
31+
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:135:5:135:34 | RepeatableAnnotation |
32+
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:136:5:136:43 | InheritedRepeatableAnnotation |
33+
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:137:5:137:46 | NonInheritedRepeatableAnnotation |
34+
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.java:142:5:142:37 | InheritedContainerAnnotation |
35+
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.java:143:5:143:38 | InheritedContainerAnnotation2 |
36+
| Annotatable.java:150:35:153:5 | {...} | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
37+
| Annotatable.java:150:35:153:5 | {...} | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
38+
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:148:5:148:44 | InheritedRepeatableAnnotation |
39+
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:150:5:153:6 | InheritedContainerAnnotation |
40+
| Annotatable.java:160:16:160:41 | NestedAnnotationContainer1 | Annotatable.java:159:5:159:14 | Inherited |
41+
| Annotatable.java:166:16:166:32 | NestedAnnotation1 | Annotatable.java:164:5:164:14 | Inherited |
42+
| Annotatable.java:166:16:166:32 | NestedAnnotation1 | Annotatable.java:165:5:165:49 | Repeatable |
43+
| Annotatable.java:172:16:172:32 | NestedAnnotation2 | Annotatable.java:170:5:170:14 | Inherited |
44+
| Annotatable.java:172:16:172:32 | NestedAnnotation2 | Annotatable.java:171:5:171:40 | Repeatable |
45+
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
46+
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.java:177:5:177:27 | NestedAnnotation2 |
47+
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
48+
annotationAdditional
49+
| Annotatable.java:20:11:20:18 | Subclass | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
50+
| Annotatable.java:26:11:26:21 | SubSubclass | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
51+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
52+
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
53+
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
54+
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
55+
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
56+
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
57+
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
58+
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
59+
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:148:5:148:44 | InheritedRepeatableAnnotation |
60+
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:150:5:153:6 | InheritedContainerAnnotation |
61+
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
62+
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.java:177:5:177:27 | NestedAnnotation2 |
63+
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
64+
bugAnnotationAdditional
65+
associatedAnnotationAdditional
66+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
67+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
68+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
69+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
70+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
71+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
72+
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
73+
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
74+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
75+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
76+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
77+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
78+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
79+
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
80+
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
81+
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
82+
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
83+
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
84+
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
85+
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
86+
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
87+
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
88+
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
89+
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
90+
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
91+
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
92+
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
93+
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
94+
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
95+
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
96+
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
97+
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
98+
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
99+
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
100+
associatedAnnotationNotInherited
101+
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |

0 commit comments

Comments
 (0)