Skip to content

Commit 5787871

Browse files
authored
Merge pull request github#3351 from hvitved/csharp/unification-nested-types
C#: Teach unification library about nested types
2 parents cc84464 + 01102b3 commit 5787871

File tree

10 files changed

+630
-360
lines changed

10 files changed

+630
-360
lines changed

csharp/ql/src/semmle/code/csharp/Implements.qll

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private module Gvn {
250250

251251
private class LeafType extends Type {
252252
LeafType() {
253-
not exists(this.getAChild()) and
253+
not this instanceof Unification::GenericType and
254254
not this instanceof MethodTypeParameter and
255255
not this instanceof DynamicType
256256
}
@@ -267,7 +267,9 @@ private module Gvn {
267267
gvnConstructedCons(_, _, _, head, tail)
268268
}
269269

270-
private ConstructedGvnTypeList gvnConstructed(Type t, Unification::CompoundTypeKind k, int i) {
270+
private ConstructedGvnTypeList gvnConstructed(
271+
Unification::GenericType t, Unification::CompoundTypeKind k, int i
272+
) {
271273
result = TConstructedGvnTypeNil(k) and
272274
i = -1 and
273275
k = Unification::getTypeKind(t)
@@ -278,14 +280,17 @@ private module Gvn {
278280
}
279281

280282
pragma[noinline]
281-
private GvnType gvnTypeChild(Type t, int i) { result = getGlobalValueNumber(t.getChild(i)) }
283+
private GvnType gvnTypeArgument(Unification::GenericType t, int i) {
284+
result = getGlobalValueNumber(t.getArgument(i))
285+
}
282286

283287
pragma[noinline]
284288
private predicate gvnConstructedCons(
285-
Type t, Unification::CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
289+
Unification::GenericType t, Unification::CompoundTypeKind k, int i, GvnType head,
290+
ConstructedGvnTypeList tail
286291
) {
287292
tail = gvnConstructed(t, k, i - 1) and
288-
head = gvnTypeChild(t, i)
293+
head = gvnTypeArgument(t, i)
289294
}
290295

291296
/** Gets the global value number for a given type. */
@@ -319,6 +324,8 @@ private module Gvn {
319324
}
320325

321326
private class ConstructedGvnTypeList extends TConstructedGvnTypeList {
327+
Unification::CompoundTypeKind getKind() { this = gvnConstructed(_, result, _) }
328+
322329
private int length() {
323330
this = TConstructedGvnTypeNil(_) and result = -1
324331
or
@@ -338,17 +345,47 @@ private module Gvn {
338345
)
339346
}
340347

348+
/**
349+
* Gets a textual representation of this constructed type, restricted
350+
* to the prefix `t` of the underlying source declaration type.
351+
*
352+
* The `toString()` calculation needs to be split up into prefixes, in
353+
* order to apply the type arguments correctly. For example, a source
354+
* declaration type `A<>.B.C<,>` applied to types `int, string, bool`
355+
* needs to be printed as `A<int>.B.C<string,bool>`.
356+
*/
357+
language[monotonicAggregates]
358+
private string toStringConstructed(Unification::GenericType t) {
359+
t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and
360+
exists(int offset, int children, string name, string nameArgs |
361+
offset = t.getNumberOfDeclaringArguments() and
362+
children = t.getNumberOfArgumentsSelf() and
363+
name = Unification::getNameNested(t) and
364+
if children = 0
365+
then nameArgs = name
366+
else
367+
exists(string offsetArgs |
368+
offsetArgs =
369+
concat(int i |
370+
i in [offset .. offset + children - 1]
371+
|
372+
this.getArg(i).toString(), "," order by i
373+
) and
374+
nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">"
375+
)
376+
|
377+
offset = 0 and result = nameArgs
378+
or
379+
result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs
380+
)
381+
}
382+
341383
language[monotonicAggregates]
342384
string toString() {
343-
exists(Unification::CompoundTypeKind k, string args |
344-
this = gvnConstructed(_, k, _) and
345-
args =
346-
concat(int i |
347-
i in [0 .. k.getNumberOfTypeParameters() - 1]
348-
|
349-
this.getArg(i).toString(), "," order by i
350-
) and
351-
result = k.toString(args)
385+
exists(Unification::CompoundTypeKind k | k = this.getKind() |
386+
result = k.toStringBuiltin(this.getArg(0).toString())
387+
or
388+
result = this.toStringConstructed(k.getConstructedSourceDeclaration())
352389
)
353390
}
354391

csharp/ql/src/semmle/code/csharp/Unification.qll

Lines changed: 137 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,75 @@ private import Caching
1010
* equal modulo identity conversions and type parameters.
1111
*/
1212
module Gvn {
13+
/**
14+
* Gets the name of type `t`, including the enclosing type of `t` as a qualifier,
15+
* but only if the enclosing type is not a `GenericType`.
16+
*/
17+
string getNameNested(Type t) {
18+
if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType
19+
then result = t.getName()
20+
else result = getNameNested(t.(NestedType).getDeclaringType()) + "." + t.getName()
21+
}
22+
23+
/**
24+
* A generic type. This is either a type with a type parameter, a type with
25+
* a type argument, or a nested type with a generic enclosing type.
26+
*
27+
* In this class, type parameters and type arguments are collectively referred
28+
* to as "arguments".
29+
*/
30+
class GenericType extends Type {
31+
GenericType() {
32+
exists(this.getChild(0))
33+
or
34+
this.(NestedType).getDeclaringType() instanceof GenericType
35+
}
36+
37+
/** Gets the generic containing type, if any. */
38+
GenericType getGenericDeclaringType() { result = this.(NestedType).getDeclaringType() }
39+
40+
/**
41+
* Gets the number of arguments of the generic containing type, or 0 if there
42+
* is no generic containing type.
43+
*/
44+
int getNumberOfDeclaringArguments() {
45+
result = this.getGenericDeclaringType().getNumberOfArguments()
46+
or
47+
not exists(this.getGenericDeclaringType()) and result = 0
48+
}
49+
50+
/** Gets the number of arguments of this type, not taking nested types into account. */
51+
int getNumberOfArgumentsSelf() { result = count(int i | exists(this.getChild(i)) and i >= 0) }
52+
53+
/** Gets the number of arguments of this type, taking nested types into account. */
54+
int getNumberOfArguments() {
55+
result = this.getNumberOfDeclaringArguments() + this.getNumberOfArgumentsSelf()
56+
}
57+
58+
/** Gets the `i`th argument of this type, taking nested types into account. */
59+
Type getArgument(int i) {
60+
result = this.getGenericDeclaringType().getArgument(i)
61+
or
62+
exists(int offset |
63+
offset = this.getNumberOfDeclaringArguments() and
64+
result = this.getChild(i - offset) and
65+
i >= offset
66+
)
67+
}
68+
69+
/** Gets a textual representation of this type, taking nested types into account. */
70+
string toStringNested() {
71+
exists(string name | name = getNameNested(this) |
72+
result = this.getGenericDeclaringType().toStringNested() + "." + name
73+
or
74+
not exists(this.getGenericDeclaringType()) and result = name
75+
)
76+
}
77+
}
78+
1379
private class LeafType extends Type {
1480
LeafType() {
15-
not exists(this.getAChild()) and
81+
not this instanceof GenericType and
1682
not this instanceof TypeParameter and
1783
not this instanceof DynamicType
1884
}
@@ -28,29 +94,37 @@ module Gvn {
2894
or
2995
this = TArrayTypeKind(_, _) and result = 1
3096
or
31-
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
32-
result = ugt.getNumberOfTypeParameters()
97+
exists(GenericType t | this = TConstructedType(t.getSourceDeclaration()) |
98+
result = t.getNumberOfArguments()
3399
)
34100
}
35101

36-
/** Gets a textual representation of this kind when applied to arguments `args`. */
102+
/** Gets the source declaration type that this kind corresponds to, if any. */
103+
GenericType getConstructedSourceDeclaration() { this = TConstructedType(result) }
104+
105+
/**
106+
* Gets a textual representation of this kind when applied to arguments `args`.
107+
*
108+
* This predicate is restricted to built-in generics (pointers, nullables, and
109+
* arrays).
110+
*/
37111
bindingset[args]
38-
string toString(string args) {
112+
string toStringBuiltin(string args) {
39113
this = TPointerTypeKind() and result = args + "*"
40114
or
41115
this = TNullableTypeKind() and result = args + "?"
42116
or
43117
exists(int rnk | this = TArrayTypeKind(_, rnk) |
44118
result = args + "[" + concat(int i | i in [0 .. rnk - 2] | ",") + "]"
45119
)
46-
or
47-
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
48-
result = ugt.getNameWithoutBrackets() + "<" + args + ">"
49-
)
50120
}
51121

52122
/** Gets a textual representation of this kind. */
53-
string toString() { result = toString("") }
123+
string toString() {
124+
result = this.toStringBuiltin("")
125+
or
126+
result = this.getConstructedSourceDeclaration().toStringNested()
127+
}
54128

55129
/** Gets the location of this kind. */
56130
Location getLocation() { result instanceof EmptyLocation }
@@ -64,11 +138,9 @@ module Gvn {
64138
or
65139
t = any(ArrayType at | result = TArrayTypeKind(at.getDimension(), at.getRank()))
66140
or
67-
result = TConstructedType(t.(ConstructedType).getUnboundGeneric())
68-
or
69-
result = TConstructedType(t.(TupleType).getUnderlyingType().getUnboundGeneric())
141+
result = TConstructedType(t.getSourceDeclaration())
70142
or
71-
result = TConstructedType(t)
143+
result = TConstructedType(t.(TupleType).getUnderlyingType().getSourceDeclaration())
72144
}
73145

74146
/**
@@ -107,7 +179,7 @@ module Gvn {
107179
override CompoundTypeKind getKind() { result = l.getKind() }
108180
}
109181

110-
private ConstructedGvnTypeList gvnConstructed(Type t, CompoundTypeKind k, int i) {
182+
private ConstructedGvnTypeList gvnConstructed(GenericType t, CompoundTypeKind k, int i) {
111183
result = TConstructedGvnTypeNil(k) and
112184
i = -1 and
113185
k = getTypeKind(t)
@@ -118,14 +190,16 @@ module Gvn {
118190
}
119191

120192
pragma[noinline]
121-
private GvnType gvnTypeChild(Type t, int i) { result = getGlobalValueNumber(t.getChild(i)) }
193+
private GvnType gvnTypeArgument(GenericType t, int i) {
194+
result = getGlobalValueNumber(t.getArgument(i))
195+
}
122196

123197
pragma[noinline]
124198
private predicate gvnConstructedCons(
125-
Type t, CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
199+
GenericType t, CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
126200
) {
127201
tail = gvnConstructed(t, k, i - 1) and
128-
head = gvnTypeChild(t, i)
202+
head = gvnTypeArgument(t, i)
129203
}
130204

131205
private class ConstructedGvnTypeList extends TConstructedGvnTypeList {
@@ -150,17 +224,47 @@ module Gvn {
150224
)
151225
}
152226

227+
/**
228+
* Gets a textual representation of this constructed type, restricted
229+
* to the prefix `t` of the underlying source declaration type.
230+
*
231+
* The `toString()` calculation needs to be split up into prefixes, in
232+
* order to apply the type arguments correctly. For example, a source
233+
* declaration type `A<>.B.C<,>` applied to types `int, string, bool`
234+
* needs to be printed as `A<int>.B.C<string,bool>`.
235+
*/
236+
language[monotonicAggregates]
237+
private string toStringConstructed(GenericType t) {
238+
t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and
239+
exists(int offset, int children, string name, string nameArgs |
240+
offset = t.getNumberOfDeclaringArguments() and
241+
children = t.getNumberOfArgumentsSelf() and
242+
name = getNameNested(t) and
243+
if children = 0
244+
then nameArgs = name
245+
else
246+
exists(string offsetArgs |
247+
offsetArgs =
248+
concat(int i |
249+
i in [offset .. offset + children - 1]
250+
|
251+
this.getArg(i).toString(), "," order by i
252+
) and
253+
nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">"
254+
)
255+
|
256+
offset = 0 and result = nameArgs
257+
or
258+
result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs
259+
)
260+
}
261+
153262
language[monotonicAggregates]
154263
string toString() {
155-
exists(CompoundTypeKind k, string args |
156-
k = this.getKind() and
157-
args =
158-
concat(int i |
159-
i in [0 .. k.getNumberOfTypeParameters() - 1]
160-
|
161-
this.getArg(i).toString(), "," order by i
162-
) and
163-
result = k.toString(args)
264+
exists(CompoundTypeKind k | k = this.getKind() |
265+
result = k.toStringBuiltin(this.getArg(0).toString())
266+
or
267+
result = this.toStringConstructed(k.getConstructedSourceDeclaration())
164268
)
165269
}
166270

@@ -366,7 +470,12 @@ module Gvn {
366470
TArrayTypeKind(int dim, int rnk) {
367471
exists(ArrayType at | dim = at.getDimension() and rnk = at.getRank())
368472
} or
369-
TConstructedType(UnboundGenericType ugt) { exists(ugt.getATypeParameter()) }
473+
TConstructedType(GenericType sourceDecl) {
474+
sourceDecl = any(GenericType t).getSourceDeclaration() and
475+
not sourceDecl instanceof PointerType and
476+
not sourceDecl instanceof NullableType and
477+
not sourceDecl instanceof ArrayType
478+
}
370479

371480
cached
372481
newtype TGvnType =

0 commit comments

Comments
 (0)