Skip to content

Commit b649ccd

Browse files
authored
Merge pull request #4761 from tamasvajk/feature/cil-enum-underlying
C#: Extract enum underlying type from IL
2 parents f95c480 + 2257a8d commit b649ccd

File tree

13 files changed

+7192
-3005
lines changed

13 files changed

+7192
-3005
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* CIL extraction has been improved to store numeric underlying type of `enum` declarations.

csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ public override IEnumerable<IExtractionProduct> Contents
212212
var @base = (Type)Cx.CreateGeneric(this, td.BaseType);
213213
yield return @base;
214214
yield return Tuples.cil_base_class(this, @base);
215+
216+
if (IsSystemEnum(td.BaseType) &&
217+
GetUnderlyingEnumType() is var underlying &&
218+
underlying.HasValue)
219+
{
220+
var underlyingType = Cx.Create(underlying.Value);
221+
yield return underlyingType;
222+
yield return Tuples.cil_enum_underlying_type(this, underlyingType);
223+
}
215224
}
216225

217226
foreach (var @interface in td.GetInterfaceImplementations().Select(i => Cx.MdReader.GetInterfaceImplementation(i)))
@@ -226,6 +235,59 @@ public override IEnumerable<IExtractionProduct> Contents
226235
}
227236
}
228237

238+
private bool IsSystemEnum(EntityHandle baseType)
239+
{
240+
return baseType.Kind switch
241+
{
242+
HandleKind.TypeReference => IsSystemEnum((TypeReferenceHandle)baseType),
243+
HandleKind.TypeDefinition => IsSystemEnum((TypeDefinitionHandle)baseType),
244+
_ => false,
245+
};
246+
}
247+
248+
private bool IsSystemEnum(TypeReferenceHandle baseType)
249+
{
250+
var baseTypeReference = Cx.MdReader.GetTypeReference(baseType);
251+
252+
return IsSystemEnum(baseTypeReference.Name, baseTypeReference.Namespace);
253+
}
254+
255+
private bool IsSystemEnum(TypeDefinitionHandle baseType)
256+
{
257+
var baseTypeDefinition = Cx.MdReader.GetTypeDefinition(baseType);
258+
259+
return IsSystemEnum(baseTypeDefinition.Name, baseTypeDefinition.Namespace);
260+
}
261+
262+
private bool IsSystemEnum(StringHandle typeName, StringHandle namespaceName)
263+
{
264+
return Cx.MdReader.StringComparer.Equals(typeName, "Enum") &&
265+
!namespaceName.IsNil &&
266+
Cx.MdReader.StringComparer.Equals(namespaceName, "System");
267+
}
268+
269+
internal PrimitiveTypeCode? GetUnderlyingEnumType()
270+
{
271+
foreach (var handle in td.GetFields())
272+
{
273+
var field = Cx.MdReader.GetFieldDefinition(handle);
274+
if (field.Attributes.HasFlag(FieldAttributes.Static))
275+
{
276+
continue;
277+
}
278+
279+
var blob = Cx.MdReader.GetBlobReader(field.Signature);
280+
if (blob.ReadSignatureHeader().Kind != SignatureKind.Field)
281+
{
282+
break;
283+
}
284+
285+
return (PrimitiveTypeCode)blob.ReadByte();
286+
}
287+
288+
return null;
289+
}
290+
229291
internal override Method LookupMethod(StringHandle name, BlobHandle signature)
230292
{
231293
foreach (var h in td.GetMethods())

csharp/extractor/Semmle.Extraction.CIL/Tuples.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ internal static Tuple cil_array_type(ArrayType array, Type element, int rank) =>
3232
internal static Tuple cil_base_class(Type t, Type @base) =>
3333
new Tuple("cil_base_class", t, @base);
3434

35+
internal static Tuple cil_enum_underlying_type(Type t, PrimitiveType underlying) =>
36+
new Tuple("cil_enum_underlying_type", t, underlying);
37+
3538
internal static Tuple cil_base_interface(Type t, Type @base) =>
3639
new Tuple("cil_base_interface", t, @base);
3740

csharp/ql/src/semmle/code/cil/Type.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
7979
}
8080

8181
/** Holds if this type is an `enum`. */
82-
predicate isEnum() { this.getBaseClass*().isSystemType("Enum") }
82+
predicate isEnum() { this.getBaseClass().isSystemType("Enum") }
8383

8484
/** Holds if this type is public. */
8585
predicate isPublic() { cil_public(this) }

csharp/ql/src/semmle/code/cil/Types.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, @cil_valueorreftype {
4747
class Enum extends ValueOrRefType {
4848
Enum() { this.isEnum() }
4949

50-
// Note that we don't actually use the proper internal representation yet.
51-
override IntType getUnderlyingType() { any() }
50+
override IntegralType getUnderlyingType() {
51+
cil_enum_underlying_type(this, result)
52+
or
53+
not cil_enum_underlying_type(this, _) and
54+
result instanceof IntType
55+
}
5256
}
5357

5458
/** A `class`. */

csharp/ql/src/semmlecode.csharp.dbscheme

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,7 @@ cil_newslot(int id: @cil_method ref);
18031803

18041804
cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref);
18051805
cil_base_interface(int id: @cil_type ref, int base: @cil_type ref);
1806+
cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref);
18061807

18071808
#keyset[unbound, index]
18081809
cil_type_parameter(

0 commit comments

Comments
 (0)