Skip to content

Commit bb735c0

Browse files
committed
C#: Teach guards library about collections
1 parent 00fe473 commit bb735c0

File tree

15 files changed

+1006
-668
lines changed

15 files changed

+1006
-668
lines changed

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 289 additions & 31 deletions
Large diffs are not rendered by default.

csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private predicate ensureNotNullAt(BasicBlock bb, int i, Ssa::Definition def) {
143143
G::Internal::asserts(bb.getNode(i).getElement(), e, v)
144144
|
145145
exprImpliesSsaDef(e, v, def, nv) and
146-
not nv.isNull()
146+
nv.isNonNull()
147147
)
148148
}
149149

@@ -258,7 +258,7 @@ private predicate defNullImpliesStep(
258258
bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode()
259259
|
260260
bb2 = bb1.getASuccessorByType(s) and
261-
not nv.isNull()
261+
nv.isNonNull()
262262
)
263263
}
264264

csharp/ql/src/semmle/code/csharp/frameworks/System.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class SystemActionTDelegateType extends SystemUnboundGenericDelegateType {
5454
/** `System.Array` class. */
5555
class SystemArrayClass extends SystemClass {
5656
SystemArrayClass() { this.hasName("Array") }
57+
58+
/** Gets the `Length` property. */
59+
Property getLengthProperty() { result = this.getProperty("Length") }
5760
}
5861

5962
/** `System.Attribute` class. */

csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,15 @@ module SystemLinq {
1818
class Class extends csharp::Class {
1919
Class() { this.getNamespace() instanceof Namespace }
2020
}
21+
22+
/** The `System.Linq.Enumerable` class. */
23+
class SystemLinqEnumerableClass extends Class {
24+
SystemLinqEnumerableClass() { this.hasName("Enumerable") }
25+
26+
/** Gets the `Count()` method. */
27+
csharp::ExtensionMethod getCountMethod() { result = this.getAMethod("Count") }
28+
29+
/** Gets the `Empty()` method. */
30+
csharp::ExtensionMethod getAnyMethod() { result = this.getAMethod("Any") }
31+
}
2132
}

csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class SystemCollectionsGenericUnboundGenericInterface extends UnboundGenericInte
1818
}
1919
}
2020

21+
/** An unbound generic class in the `System.Collections.Generic` namespace. */
22+
class SystemCollectionsGenericUnboundGenericClass extends UnboundGenericClass {
23+
SystemCollectionsGenericUnboundGenericClass() {
24+
this.getNamespace() instanceof SystemCollectionsGenericNamespace
25+
}
26+
}
27+
2128
/** An unbound generic struct in the `System.Collections.Generic` namespace. */
2229
class SystemCollectionsGenericUnboundGenericStruct extends UnboundGenericStruct {
2330
SystemCollectionsGenericUnboundGenericStruct() {
@@ -86,6 +93,18 @@ class SystemCollectionsGenericIListTInterface extends SystemCollectionsGenericUn
8693
}
8794
}
8895

96+
/** The `System.Collections.Generic.List<T>` class. */
97+
class SystemCollectionsGenericListClass extends SystemCollectionsGenericUnboundGenericClass {
98+
SystemCollectionsGenericListClass() {
99+
this.hasName("List<>") and
100+
this.getNumberOfTypeParameters() = 1
101+
}
102+
103+
Method getClearMethod() { result = this.getAMethod("Clear") }
104+
105+
Method getAddMethod() { result = this.getAMethod("Add") }
106+
}
107+
89108
/** The `System.Collections.Generic.KeyValuePair<TKey, TValue>` structure. */
90109
class SystemCollectionsGenericKeyValuePairStruct extends SystemCollectionsGenericUnboundGenericStruct {
91110
SystemCollectionsGenericKeyValuePairStruct() {
@@ -111,4 +130,7 @@ class SystemCollectionsGenericKeyValuePairStruct extends SystemCollectionsGeneri
111130
/** The `System.Collections.Generic.ICollection<>` interface. */
112131
class SystemCollectionsGenericICollectionInterface extends SystemCollectionsGenericUnboundGenericInterface {
113132
SystemCollectionsGenericICollectionInterface() { this.hasName("ICollection<>") }
133+
134+
/** Gets the `Count` property. */
135+
Property getCountProperty() { result = this.getProperty("Count") }
114136
}
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
abstractValue
2+
| 0 | Collections.cs:12:32:12:32 | 0 |
3+
| 0 | Collections.cs:14:28:14:28 | 0 |
4+
| 0 | Collections.cs:16:27:16:27 | 0 |
5+
| 0 | Collections.cs:17:28:17:28 | 0 |
6+
| 0 | Collections.cs:23:31:23:31 | 0 |
7+
| 0 | Collections.cs:25:27:25:27 | 0 |
8+
| 0 | Collections.cs:27:26:27:26 | 0 |
9+
| 0 | Collections.cs:28:27:28:27 | 0 |
10+
| 0 | Collections.cs:34:33:34:33 | 0 |
11+
| 0 | Collections.cs:36:29:36:29 | 0 |
12+
| 0 | Collections.cs:38:28:38:28 | 0 |
13+
| 0 | Collections.cs:39:29:39:29 | 0 |
14+
| 0 | Collections.cs:50:27:50:27 | 0 |
15+
| 0 | Collections.cs:57:24:57:24 | 0 |
16+
| 0 | Collections.cs:65:24:65:24 | 0 |
17+
| 0 | Guards.cs:12:24:12:24 | 0 |
18+
| 0 | Guards.cs:78:26:78:26 | 0 |
19+
| 0 | Guards.cs:80:25:80:25 | 0 |
20+
| 0 | Guards.cs:82:26:82:26 | 0 |
21+
| 0 | Guards.cs:92:30:92:30 | 0 |
22+
| 0 | Guards.cs:241:17:241:17 | 0 |
23+
| 0 | Guards.cs:255:17:255:19 | access to constant A |
24+
| 0 | Guards.cs:298:21:298:21 | 0 |
25+
| 0 | Guards.cs:310:21:310:21 | 0 |
26+
| 0 | Guards.cs:317:17:317:17 | 0 |
27+
| 0 | Guards.cs:322:18:322:18 | 0 |
28+
| 0 | Guards.cs:329:17:329:19 | access to constant A |
29+
| 0 | Guards.cs:334:20:334:20 | 0 |
30+
| 0 | Splitting.cs:136:20:136:20 | 0 |
31+
| 1 | Collections.cs:13:28:13:28 | 1 |
32+
| 1 | Collections.cs:15:28:15:28 | 1 |
33+
| 1 | Collections.cs:18:28:18:28 | 1 |
34+
| 1 | Collections.cs:24:27:24:27 | 1 |
35+
| 1 | Collections.cs:26:27:26:27 | 1 |
36+
| 1 | Collections.cs:29:27:29:27 | 1 |
37+
| 1 | Collections.cs:35:29:35:29 | 1 |
38+
| 1 | Collections.cs:37:29:37:29 | 1 |
39+
| 1 | Collections.cs:40:29:40:29 | 1 |
40+
| 1 | Guards.cs:92:25:92:25 | 1 |
41+
| 1 | Guards.cs:243:17:243:17 | 1 |
42+
| 1 | Guards.cs:246:18:246:18 | 1 |
43+
| 1 | Guards.cs:257:17:257:19 | access to constant B |
44+
| 1 | Guards.cs:260:18:260:20 | access to constant B |
45+
| 1 | Guards.cs:299:18:299:18 | 1 |
46+
| 1 | Guards.cs:311:18:311:18 | 1 |
47+
| 1 | Guards.cs:319:17:319:17 | 1 |
48+
| 1 | Guards.cs:322:13:322:13 | 1 |
49+
| 1 | Guards.cs:323:18:323:18 | 1 |
50+
| 1 | Guards.cs:331:17:331:19 | access to constant B |
51+
| 1 | Guards.cs:334:13:334:15 | access to constant B |
52+
| 1 | Guards.cs:335:18:335:18 | 1 |
53+
| 3 | Collections.cs:55:13:55:41 | 3 |
54+
| 3 | Collections.cs:63:17:63:45 | 3 |
55+
| 10 | Guards.cs:84:25:84:26 | 10 |
56+
| 10 | Guards.cs:86:26:86:27 | 10 |
57+
| empty | Collections.cs:54:13:54:16 | access to parameter args |
58+
| empty | Collections.cs:57:9:57:25 | ... = ... |
59+
| empty | Collections.cs:57:13:57:25 | array creation of type String[] |
60+
| empty | Collections.cs:58:9:58:13 | ... = ... |
61+
| empty | Collections.cs:58:13:58:13 | access to local variable x |
62+
| empty | Collections.cs:65:13:65:13 | access to local variable x |
63+
| false | Guards.cs:178:16:178:20 | false |
64+
| false | Guards.cs:181:52:181:56 | false |
65+
| false | Guards.cs:217:18:217:22 | false |
66+
| false | Guards.cs:228:18:228:22 | false |
67+
| false | Guards.cs:295:18:295:22 | false |
68+
| false | Guards.cs:305:18:305:22 | false |
69+
| non-empty | Collections.cs:55:9:55:41 | ... = ... |
70+
| non-empty | Collections.cs:55:13:55:41 | array creation of type String[] |
71+
| non-empty | Collections.cs:56:9:56:13 | ... = ... |
72+
| non-empty | Collections.cs:56:13:56:13 | access to local variable x |
73+
| non-empty | Collections.cs:63:17:63:45 | array creation of type String[] |
74+
| non-empty | Collections.cs:68:13:68:13 | access to local variable x |
75+
| non-null | Assert.cs:9:31:9:32 | "" |
76+
| non-null | Assert.cs:16:31:16:32 | "" |
77+
| non-null | Assert.cs:23:31:23:32 | "" |
78+
| non-null | Assert.cs:30:31:30:32 | "" |
79+
| non-null | Assert.cs:37:31:37:32 | "" |
80+
| non-null | Assert.cs:44:31:44:32 | "" |
81+
| non-null | Assert.cs:51:31:51:32 | "" |
82+
| non-null | Assert.cs:58:31:58:32 | "" |
83+
| non-null | Assert.cs:65:31:65:32 | "" |
84+
| non-null | Assert.cs:72:31:72:32 | "" |
85+
| non-null | Assert.cs:79:31:79:32 | "" |
86+
| non-null | Collections.cs:55:9:55:41 | ... = ... |
87+
| non-null | Collections.cs:55:13:55:41 | array creation of type String[] |
88+
| non-null | Collections.cs:55:27:55:29 | "a" |
89+
| non-null | Collections.cs:55:32:55:34 | "b" |
90+
| non-null | Collections.cs:55:37:55:39 | "c" |
91+
| non-null | Collections.cs:56:9:56:13 | ... = ... |
92+
| non-null | Collections.cs:56:13:56:13 | access to local variable x |
93+
| non-null | Collections.cs:57:9:57:25 | ... = ... |
94+
| non-null | Collections.cs:57:13:57:25 | array creation of type String[] |
95+
| non-null | Collections.cs:58:9:58:13 | ... = ... |
96+
| non-null | Collections.cs:58:13:58:13 | access to local variable x |
97+
| non-null | Collections.cs:63:17:63:45 | array creation of type String[] |
98+
| non-null | Collections.cs:63:31:63:33 | "a" |
99+
| non-null | Collections.cs:63:36:63:38 | "b" |
100+
| non-null | Collections.cs:63:41:63:43 | "c" |
101+
| non-null | Collections.cs:67:19:67:21 | "a" |
102+
| non-null | Collections.cs:68:19:68:21 | "b" |
103+
| non-null | Guards.cs:18:31:18:46 | "<empty string>" |
104+
| non-null | Guards.cs:33:31:33:35 | ... + ... |
105+
| non-null | Guards.cs:36:32:36:36 | ... + ... |
106+
| non-null | Guards.cs:39:31:39:35 | ... + ... |
107+
| non-null | Guards.cs:42:32:42:36 | ... + ... |
108+
| non-null | Guards.cs:44:13:44:17 | this access |
109+
| non-null | Guards.cs:45:31:45:42 | object creation of type Guards |
110+
| non-null | Guards.cs:47:13:47:17 | this access |
111+
| non-null | Guards.cs:48:31:48:34 | this access |
112+
| non-null | Guards.cs:61:19:61:33 | object creation of type Exception |
113+
| non-null | Guards.cs:78:26:78:26 | (...) ... |
114+
| non-null | Guards.cs:80:25:80:25 | (...) ... |
115+
| non-null | Guards.cs:82:26:82:26 | (...) ... |
116+
| non-null | Guards.cs:84:25:84:26 | (...) ... |
117+
| non-null | Guards.cs:86:26:86:27 | (...) ... |
118+
| non-null | Guards.cs:92:25:92:25 | (...) ... |
119+
| non-null | Guards.cs:92:30:92:30 | (...) ... |
120+
| non-null | Guards.cs:96:18:96:19 | "" |
121+
| non-null | Guards.cs:105:19:105:33 | object creation of type Exception |
122+
| non-null | Guards.cs:183:37:183:48 | this access |
123+
| non-null | Guards.cs:189:14:189:25 | this access |
124+
| non-null | Guards.cs:191:14:191:25 | this access |
125+
| non-null | Guards.cs:193:14:193:25 | this access |
126+
| non-null | Guards.cs:195:13:195:27 | this access |
127+
| non-null | Guards.cs:197:14:197:29 | this access |
128+
| non-null | Guards.cs:268:30:268:41 | call to method GetType |
129+
| non-null | Splitting.cs:33:24:33:25 | "" |
130+
| non-null | Splitting.cs:132:21:132:29 | this access |
131+
| null | Assert.cs:9:24:9:27 | null |
132+
| null | Assert.cs:10:27:10:30 | null |
133+
| null | Assert.cs:16:24:16:27 | null |
134+
| null | Assert.cs:23:24:23:27 | null |
135+
| null | Assert.cs:30:24:30:27 | null |
136+
| null | Assert.cs:31:28:31:31 | null |
137+
| null | Assert.cs:37:24:37:27 | null |
138+
| null | Assert.cs:38:28:38:31 | null |
139+
| null | Assert.cs:44:24:44:27 | null |
140+
| null | Assert.cs:45:29:45:32 | null |
141+
| null | Assert.cs:51:24:51:27 | null |
142+
| null | Assert.cs:52:29:52:32 | null |
143+
| null | Assert.cs:58:24:58:27 | null |
144+
| null | Assert.cs:59:28:59:31 | null |
145+
| null | Assert.cs:65:24:65:27 | null |
146+
| null | Assert.cs:66:29:66:32 | null |
147+
| null | Assert.cs:72:24:72:27 | null |
148+
| null | Assert.cs:73:28:73:31 | null |
149+
| null | Assert.cs:79:24:79:27 | null |
150+
| null | Assert.cs:80:29:80:32 | null |
151+
| null | Guards.cs:10:21:10:24 | null |
152+
| null | Guards.cs:24:18:24:21 | null |
153+
| null | Guards.cs:32:47:32:50 | null |
154+
| null | Guards.cs:35:18:35:21 | null |
155+
| null | Guards.cs:35:31:35:34 | null |
156+
| null | Guards.cs:38:20:38:23 | null |
157+
| null | Guards.cs:38:33:38:36 | null |
158+
| null | Guards.cs:41:22:41:25 | null |
159+
| null | Guards.cs:41:35:41:38 | null |
160+
| null | Guards.cs:44:22:44:25 | null |
161+
| null | Guards.cs:47:22:47:25 | null |
162+
| null | Guards.cs:53:24:53:27 | null |
163+
| null | Guards.cs:60:42:60:45 | null |
164+
| null | Guards.cs:68:21:68:24 | null |
165+
| null | Guards.cs:71:13:71:20 | ... = ... |
166+
| null | Guards.cs:71:17:71:20 | null |
167+
| null | Guards.cs:72:31:72:31 | access to parameter s |
168+
| null | Guards.cs:88:26:88:29 | null |
169+
| null | Guards.cs:104:42:104:45 | null |
170+
| null | Guards.cs:106:9:106:25 | ... = ... |
171+
| null | Guards.cs:106:22:106:25 | null |
172+
| null | Guards.cs:115:52:115:55 | null |
173+
| null | Guards.cs:117:9:117:25 | ... = ... |
174+
| null | Guards.cs:117:22:117:25 | null |
175+
| null | Guards.cs:172:38:172:41 | null |
176+
| null | Guards.cs:181:38:181:41 | null |
177+
| null | Guards.cs:185:42:185:45 | null |
178+
| null | Guards.cs:203:18:203:21 | null |
179+
| null | Splitting.cs:12:22:12:25 | null |
180+
| null | Splitting.cs:22:22:22:25 | null |
181+
| null | Splitting.cs:32:22:32:25 | null |
182+
| null | Splitting.cs:41:18:41:21 | null |
183+
| null | Splitting.cs:54:18:54:21 | null |
184+
| null | Splitting.cs:65:18:65:21 | null |
185+
| null | Splitting.cs:76:18:76:21 | null |
186+
| null | Splitting.cs:87:31:87:34 | null |
187+
| null | Splitting.cs:97:31:97:34 | null |
188+
| null | Splitting.cs:105:27:105:30 | null |
189+
| null | Splitting.cs:116:27:116:30 | null |
190+
| null | Splitting.cs:125:20:125:23 | null |
191+
| null | Splitting.cs:128:22:128:25 | null |
192+
| true | Guards.cs:177:20:177:23 | true |
193+
| true | Guards.cs:181:45:181:48 | true |
194+
| true | Guards.cs:185:49:185:52 | true |
195+
| true | Guards.cs:185:56:185:59 | true |
196+
| true | Guards.cs:215:18:215:21 | true |
197+
| true | Guards.cs:220:18:220:21 | true |
198+
| true | Guards.cs:230:18:230:21 | true |
199+
| true | Guards.cs:233:18:233:21 | true |
200+
| true | Guards.cs:293:18:293:21 | true |
201+
| true | Guards.cs:298:13:298:16 | true |
202+
| true | Guards.cs:307:18:307:21 | true |
203+
| true | Guards.cs:310:13:310:16 | true |
204+
dualValue
205+
| empty | non-empty |
206+
| false | true |
207+
| match 1 | non-match 1 |
208+
| match 1 | non-match 1 |
209+
| match "" | non-match "" |
210+
| match "" | non-match "" |
211+
| match Action<String> a | non-match Action<String> a |
212+
| match Action<String> a | non-match Action<String> a |
213+
| match _ | non-match _ |
214+
| match _ | non-match _ |
215+
| match _ | non-match _ |
216+
| match _ | non-match _ |
217+
| match _ | non-match _ |
218+
| match access to constant B | non-match access to constant B |
219+
| match access to constant B | non-match access to constant B |
220+
| match access to type Action<Object> | non-match access to type Action<Object> |
221+
| match access to type Action<Object> | non-match access to type Action<Object> |
222+
| match null | non-match null |
223+
| match null | non-match null |
224+
| match true | non-match true |
225+
| match true | non-match true |
226+
| match true | non-match true |
227+
| match true | non-match true |
228+
| non-empty | empty |
229+
| non-match 1 | match 1 |
230+
| non-match 1 | match 1 |
231+
| non-match "" | match "" |
232+
| non-match "" | match "" |
233+
| non-match Action<String> a | match Action<String> a |
234+
| non-match Action<String> a | match Action<String> a |
235+
| non-match _ | match _ |
236+
| non-match _ | match _ |
237+
| non-match _ | match _ |
238+
| non-match _ | match _ |
239+
| non-match _ | match _ |
240+
| non-match access to constant B | match access to constant B |
241+
| non-match access to constant B | match access to constant B |
242+
| non-match access to type Action<Object> | match access to type Action<Object> |
243+
| non-match access to type Action<Object> | match access to type Action<Object> |
244+
| non-match null | match null |
245+
| non-match null | match null |
246+
| non-match true | match true |
247+
| non-match true | match true |
248+
| non-match true | match true |
249+
| non-match true | match true |
250+
| non-null | null |
251+
| null | non-null |
252+
| true | false |
253+
singletonValue
254+
| 0 |
255+
| 1 |
256+
| 3 |
257+
| 10 |
258+
| false |
259+
| null |
260+
| true |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import csharp
2+
private import semmle.code.csharp.controlflow.Guards
3+
4+
query predicate abstractValue(AbstractValue value, Expr e) { e = value.getAnExpr() }
5+
6+
query predicate dualValue(AbstractValue value, AbstractValue dual) { dual = value.getDualValue() }
7+
8+
query predicate singletonValue(AbstractValue value) { value.isSingleton() }

csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true |
1616
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false |
1717
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false |
18+
| Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false |
19+
| Collections.cs:53:9:53:12 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false |
20+
| Collections.cs:54:13:54:16 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false |
21+
| Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true |
22+
| Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true |
1823
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false |
1924
| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false |
2025
| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true |

0 commit comments

Comments
 (0)