mirror of
https://github.com/github/codeql.git
synced 2026-04-22 23:35:14 +02:00
C#: Extract generic types in CIL attribute extraction
This commit is contained in:
@@ -70,10 +70,6 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
return h;
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
|
||||
|
||||
public override IEnumerable<IExtractionProduct> Contents
|
||||
{
|
||||
get
|
||||
@@ -98,8 +94,6 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public override Namespace ContainingNamespace => unboundGenericType.ContainingNamespace!;
|
||||
|
||||
public override int ThisTypeParameterCount => thisTypeArguments == null ? 0 : thisTypeArguments.Length;
|
||||
|
||||
public override CilTypeKind Kind => unboundGenericType.Kind;
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments)
|
||||
@@ -114,6 +108,12 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) => unboundGenericType.WriteAssemblyPrefix(trapFile);
|
||||
|
||||
public override int ThisTypeParameterCount => thisTypeArguments?.Length ?? 0;
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => GenericArguments;
|
||||
|
||||
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal class GenericsHelper
|
||||
internal static class GenericsHelper
|
||||
{
|
||||
public static TypeTypeParameter[] MakeTypeParameters(Type container, int count)
|
||||
{
|
||||
@@ -22,7 +23,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public static string GetNonGenericName(string name)
|
||||
{
|
||||
var tick = name.IndexOf('`');
|
||||
var tick = name.LastIndexOf('`');
|
||||
return tick == -1
|
||||
? name
|
||||
: name.Substring(0, tick);
|
||||
@@ -36,10 +37,23 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public static int GetGenericTypeParameterCount(string name)
|
||||
{
|
||||
var tick = name.IndexOf('`');
|
||||
var tick = name.LastIndexOf('`');
|
||||
return tick == -1
|
||||
? 0
|
||||
: int.Parse(name.Substring(tick + 1));
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetAllTypeParameters(Type? container, IEnumerable<TypeTypeParameter> thisTypeParameters)
|
||||
{
|
||||
if (container != null)
|
||||
{
|
||||
foreach (var t in container.TypeParameters)
|
||||
yield return t;
|
||||
}
|
||||
|
||||
foreach (var t in thisTypeParameters)
|
||||
yield return t;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal sealed partial class NoMetadataHandleType
|
||||
{
|
||||
/// <summary>
|
||||
/// Parser to split a fully qualified name into short name, namespace or declaring type name, assembly name, and
|
||||
/// type argument names. Names are in the following format:
|
||||
/// <c>N1.N2.T1`2+T2`2[T3,[T4, A1, Version=...],T5,T6], A2, Version=...</c>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>typeof(System.Collections.Generic.List<int>.Enumerator)
|
||||
/// -> System.Collections.Generic.List`1+Enumerator[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
|
||||
/// typeof(System.Collections.Generic.List<>.Enumerator)
|
||||
/// -> System.Collections.Generic.List`1+Enumerator, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
|
||||
/// </code>
|
||||
/// </example>
|
||||
private class FullyQualifiedNameParser
|
||||
{
|
||||
public string ShortName { get; internal set; }
|
||||
public string? AssemblyName { get; internal set; }
|
||||
public IEnumerable<string>? TypeArguments { get; internal set; }
|
||||
public string? UnboundGenericTypeName { get; internal set; }
|
||||
public string ContainerName { get; internal set; }
|
||||
public bool IsContainerNamespace { get; internal set; }
|
||||
|
||||
private string AssemblySuffix => string.IsNullOrWhiteSpace(AssemblyName) ? "" : $", {AssemblyName}";
|
||||
|
||||
public FullyQualifiedNameParser(string name)
|
||||
{
|
||||
ExtractAssemblyName(ref name, out var lastBracketIndex);
|
||||
ExtractTypeArguments(ref name, lastBracketIndex, out var containerTypeArguments);
|
||||
ExtractContainer(ref name, containerTypeArguments);
|
||||
|
||||
ShortName = name;
|
||||
}
|
||||
|
||||
private void ExtractTypeArguments(ref string name, int lastBracketIndex, out string containerTypeArguments)
|
||||
{
|
||||
var firstBracketIndex = name.IndexOf('[');
|
||||
if (firstBracketIndex < 0)
|
||||
{
|
||||
// not generic or non-constructed generic
|
||||
TypeArguments = null;
|
||||
containerTypeArguments = "";
|
||||
UnboundGenericTypeName = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// "T3,[T4, Assembly1, Version=...],T5,T6"
|
||||
string typeArgs;
|
||||
(name, _, typeArgs, _) = name.Split(firstBracketIndex, firstBracketIndex + 1, lastBracketIndex - firstBracketIndex - 1);
|
||||
|
||||
var thisTypeArgCount = GenericsHelper.GetGenericTypeParameterCount(name);
|
||||
if (thisTypeArgCount == 0)
|
||||
{
|
||||
// not generic or non-constructed generic; container is constructed
|
||||
TypeArguments = null;
|
||||
containerTypeArguments = $"[{typeArgs}]";
|
||||
UnboundGenericTypeName = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// constructed generic
|
||||
// "T3,[T4, Assembly1, Version=...]", ["T5", "T6"]
|
||||
var (containerTypeArgs, thisTypeArgs) = ParseTypeArgumentStrings(typeArgs, thisTypeArgCount);
|
||||
|
||||
TypeArguments = thisTypeArgs;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(containerTypeArgs))
|
||||
{
|
||||
// containing type is not constructed generics
|
||||
containerTypeArguments = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
// "T3,[T4, Assembly1, Version=...],,]"
|
||||
containerTypeArguments = $"[{containerTypeArgs}]";
|
||||
}
|
||||
|
||||
UnboundGenericTypeName = $"{name}{AssemblySuffix}";
|
||||
}
|
||||
|
||||
private void ExtractContainer(ref string name, string containerTypeArguments)
|
||||
{
|
||||
var lastPlusIndex = name.LastIndexOf('+');
|
||||
IsContainerNamespace = lastPlusIndex < 0;
|
||||
if (IsContainerNamespace)
|
||||
{
|
||||
ExtractContainerNamespace(ref name);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtractContainerType(ref name, containerTypeArguments, lastPlusIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractContainerNamespace(ref string name)
|
||||
{
|
||||
var lastDotIndex = name.LastIndexOf('.');
|
||||
if (lastDotIndex >= 0)
|
||||
{
|
||||
(ContainerName, _, name) = name.Split(lastDotIndex, lastDotIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContainerName = ""; // global namespace name
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractContainerType(ref string name, string containerTypeArguments, int lastPlusIndex)
|
||||
{
|
||||
(ContainerName, _, name) = name.Split(lastPlusIndex, lastPlusIndex + 1);
|
||||
ContainerName = $"{ContainerName}{containerTypeArguments}{AssemblySuffix}";
|
||||
}
|
||||
|
||||
private void ExtractAssemblyName(ref string name, out int lastBracketIndex)
|
||||
{
|
||||
lastBracketIndex = name.LastIndexOf(']');
|
||||
var assemblyCommaIndex = name.IndexOf(',', lastBracketIndex < 0 ? 0 : lastBracketIndex);
|
||||
if (assemblyCommaIndex >= 0)
|
||||
{
|
||||
// "Assembly2, Version=..."
|
||||
(name, _, AssemblyName) = name.Split(assemblyCommaIndex, assemblyCommaIndex + 2);
|
||||
}
|
||||
}
|
||||
|
||||
private static (string, IEnumerable<string>) ParseTypeArgumentStrings(string typeArgs, int thisTypeArgCount)
|
||||
{
|
||||
var thisTypeArgs = new Stack<string>(thisTypeArgCount);
|
||||
while (typeArgs.Length > 0 && thisTypeArgCount > 0)
|
||||
{
|
||||
int startCurrentType;
|
||||
if (typeArgs[^1] != ']')
|
||||
{
|
||||
startCurrentType = typeArgs.LastIndexOf(',') + 1;
|
||||
thisTypeArgs.Push(typeArgs.Substring(startCurrentType));
|
||||
}
|
||||
else
|
||||
{
|
||||
var bracketCount = 1;
|
||||
for (startCurrentType = typeArgs.Length - 2; startCurrentType >= 0 && bracketCount > 0; startCurrentType--)
|
||||
{
|
||||
if (typeArgs[startCurrentType] == ']')
|
||||
{
|
||||
bracketCount++;
|
||||
}
|
||||
else if (typeArgs[startCurrentType] == '[')
|
||||
{
|
||||
bracketCount--;
|
||||
}
|
||||
}
|
||||
startCurrentType++;
|
||||
thisTypeArgs.Push(typeArgs[(startCurrentType + 1)..^1]);
|
||||
}
|
||||
|
||||
if (startCurrentType != 0)
|
||||
{
|
||||
typeArgs = typeArgs.Substring(0, startCurrentType - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeArgs = "";
|
||||
}
|
||||
thisTypeArgCount--;
|
||||
}
|
||||
return (typeArgs, thisTypeArgs.ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal sealed class NoMetadataHandleType : Type
|
||||
internal sealed partial class NoMetadataHandleType : Type
|
||||
{
|
||||
private readonly string originalName;
|
||||
private readonly string name;
|
||||
@@ -15,87 +15,56 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
private readonly string containerName;
|
||||
private readonly bool isContainerNamespace;
|
||||
|
||||
private readonly Lazy<TypeTypeParameter[]> typeParams;
|
||||
private readonly Lazy<TypeTypeParameter[]>? typeParams;
|
||||
|
||||
// Either null or notEmpty
|
||||
private readonly Type[]? thisTypeArguments;
|
||||
private readonly Type unboundGenericType;
|
||||
private readonly Type? containingType;
|
||||
|
||||
private readonly NamedTypeIdWriter idWriter;
|
||||
|
||||
public NoMetadataHandleType(Context cx, string originalName) : base(cx)
|
||||
{
|
||||
this.originalName = originalName;
|
||||
this.name = originalName;
|
||||
this.idWriter = new NamedTypeIdWriter(this);
|
||||
|
||||
// N1.N2.T1`3+T2`1[T3,[T4, Assembly1, Version=...],T5,T6], Assembly2, Version=...
|
||||
// for example:
|
||||
// typeof(System.Collections.Generic.List<int>.Enumerator)
|
||||
// -> System.Collections.Generic.List`1+Enumerator[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
|
||||
// typeof(System.Collections.Generic.List<>.Enumerator)
|
||||
// -> System.Collections.Generic.List`1+Enumerator, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
|
||||
var nameParser = new FullyQualifiedNameParser(originalName);
|
||||
|
||||
var lastBracketIndex = name.LastIndexOf(']');
|
||||
var assemblyCommaIndex = name.IndexOf(',', lastBracketIndex < 0 ? 0 : lastBracketIndex);
|
||||
if (assemblyCommaIndex >= 0)
|
||||
name = nameParser.ShortName;
|
||||
assemblyName = nameParser.AssemblyName;
|
||||
isContainerNamespace = nameParser.IsContainerNamespace;
|
||||
containerName = nameParser.ContainerName;
|
||||
|
||||
unboundGenericType = nameParser.UnboundGenericTypeName == null
|
||||
? this
|
||||
: new NoMetadataHandleType(Cx, nameParser.UnboundGenericTypeName);
|
||||
|
||||
if (nameParser.TypeArguments != null)
|
||||
{
|
||||
assemblyName = name.Substring(assemblyCommaIndex + 2);
|
||||
name = name.Substring(0, assemblyCommaIndex);
|
||||
}
|
||||
|
||||
var firstBracketIndex = name.IndexOf('[');
|
||||
if (firstBracketIndex >= 0)
|
||||
{
|
||||
// TODO:
|
||||
// * create types for arguments.
|
||||
// * this type is a constructed generic -> create non constructed one too.
|
||||
// * adjust containing type name, which can also be a constructed generic
|
||||
|
||||
// thisTypeArguments =
|
||||
// unboundGenericType =
|
||||
// containerName =
|
||||
|
||||
throw new NotImplementedException();
|
||||
// name = name.Substring(0, firstBracketIndex);
|
||||
thisTypeArguments = nameParser.TypeArguments.Select(t => new NoMetadataHandleType(Cx, t)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
thisTypeArguments = null;
|
||||
unboundGenericType = this;
|
||||
typeParams = new Lazy<TypeTypeParameter[]>(GenericsHelper.MakeTypeParameters(this, ThisTypeParameterCount));
|
||||
}
|
||||
|
||||
var lastPlusIndex = name.LastIndexOf('+');
|
||||
if (lastPlusIndex >= 0)
|
||||
{
|
||||
containerName = name.Substring(0, lastPlusIndex);
|
||||
name = name.Substring(lastPlusIndex + 1);
|
||||
isContainerNamespace = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastDotIndex = name.LastIndexOf('.');
|
||||
if (lastDotIndex >= 0)
|
||||
{
|
||||
containerName = name.Substring(0, lastDotIndex);
|
||||
name = name.Substring(lastDotIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerName = cx.GlobalNamespace.Name;
|
||||
}
|
||||
isContainerNamespace = true;
|
||||
}
|
||||
containingType = isContainerNamespace
|
||||
? null
|
||||
: new NoMetadataHandleType(Cx, containerName);
|
||||
|
||||
this.typeParams = new Lazy<TypeTypeParameter[]>(GenericsHelper.MakeTypeParameters(this, ThisTypeParameterCount));
|
||||
Populate();
|
||||
}
|
||||
|
||||
private void Populate()
|
||||
{
|
||||
if (isContainerNamespace &&
|
||||
!ContainingNamespace!.IsGlobalNamespace)
|
||||
{
|
||||
cx.Populate(ContainingNamespace);
|
||||
Cx.Populate(ContainingNamespace);
|
||||
}
|
||||
|
||||
cx.Populate(this);
|
||||
Cx.Populate(this);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -112,7 +81,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var tp in typeParams.Value)
|
||||
foreach (var tp in typeParams?.Value ?? Array.Empty<TypeTypeParameter>())
|
||||
yield return tp;
|
||||
|
||||
foreach (var c in base.Contents)
|
||||
@@ -137,19 +106,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
: new Namespace(Cx, containerName)
|
||||
: null;
|
||||
|
||||
public override Type? ContainingType => isContainerNamespace
|
||||
? null
|
||||
: new NoMetadataHandleType(
|
||||
Cx,
|
||||
string.IsNullOrWhiteSpace(assemblyName)
|
||||
? containerName
|
||||
: containerName + ", " + assemblyName);
|
||||
|
||||
public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(name);
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => typeParams.Value;
|
||||
|
||||
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
|
||||
public override Type? ContainingType => containingType;
|
||||
|
||||
public override Type SourceDeclaration => unboundGenericType;
|
||||
|
||||
@@ -181,5 +138,21 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
idWriter.WriteId(trapFile, inContext);
|
||||
}
|
||||
|
||||
public override int ThisTypeParameterCount => unboundGenericType == this
|
||||
? GenericsHelper.GetGenericTypeParameterCount(name)
|
||||
: thisTypeArguments!.Length;
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => unboundGenericType == this
|
||||
? GenericsHelper.GetAllTypeParameters(containingType, typeParams!.Value)
|
||||
: GenericArguments;
|
||||
|
||||
public override IEnumerable<Type> ThisTypeArguments => unboundGenericType == this
|
||||
? base.ThisTypeArguments
|
||||
: thisTypeArguments!;
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments => unboundGenericType == this
|
||||
? typeParams!.Value
|
||||
: thisTypeArguments!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,29 +31,6 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> TypeArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ContainingType != null)
|
||||
{
|
||||
foreach (var t in ContainingType.TypeArguments)
|
||||
yield return t;
|
||||
}
|
||||
foreach (var t in ThisTypeArguments)
|
||||
yield return t;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Type> ThisTypeArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the assembly identifier of this type.
|
||||
/// </summary>
|
||||
@@ -109,13 +86,25 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
public abstract Type Construct(IEnumerable<Type> typeArguments);
|
||||
|
||||
/// <summary>
|
||||
/// The number of type arguments, or 0 if this isn't generic.
|
||||
/// The containing type may also have type arguments.
|
||||
/// Returns the type arguments of constructed types. For non-constructed types it returns an
|
||||
/// empty collection.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<Type> ThisTypeArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of type parameters for non-constructed generic types, the number of type arguments
|
||||
/// for constructed types, or 0.
|
||||
/// </summary>
|
||||
public abstract int ThisTypeParameterCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The total number of type parameters (including parent types).
|
||||
/// The total number of type parameters/type arguments (including parent types).
|
||||
/// This is used for internal consistency checking only.
|
||||
/// </summary>
|
||||
public int TotalTypeParametersCount => ContainingType == null
|
||||
@@ -123,8 +112,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
: ThisTypeParameterCount + ContainingType.TotalTypeParametersCount;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all bound/unbound generic arguments
|
||||
/// of a constructed/unbound generic type.
|
||||
/// Returns all bound/unbound generic arguments of a constructed/unbound generic type.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<Type> ThisGenericArguments
|
||||
{
|
||||
|
||||
@@ -53,18 +53,6 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public override Type? ContainingType => declType;
|
||||
|
||||
public override int ThisTypeParameterCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var containingType = td.GetDeclaringType();
|
||||
var parentTypeParameters = containingType.IsNil ? 0 :
|
||||
Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count;
|
||||
|
||||
return td.GetGenericParameters().Count - parentTypeParameters;
|
||||
}
|
||||
}
|
||||
|
||||
public override CilTypeKind Kind => CilTypeKind.ValueOrRefType;
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments)
|
||||
@@ -103,21 +91,23 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
return newTypeParams;
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> TypeParameters
|
||||
public override int ThisTypeParameterCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (declType != null)
|
||||
{
|
||||
foreach (var t in declType.TypeParameters)
|
||||
yield return t;
|
||||
}
|
||||
var containingType = td.GetDeclaringType();
|
||||
var parentTypeParameters = containingType.IsNil
|
||||
? 0
|
||||
: Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count;
|
||||
|
||||
foreach (var t in typeParams.Value)
|
||||
yield return t;
|
||||
return td.GetGenericParameters().Count - parentTypeParameters;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => GenericsHelper.GetAllTypeParameters(declType, typeParams!.Value);
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments => typeParams.Value;
|
||||
|
||||
public override IEnumerable<IExtractionProduct> Contents
|
||||
{
|
||||
get
|
||||
|
||||
@@ -52,17 +52,6 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public override Namespace ContainingNamespace => Cx.CreateNamespace(tr.Namespace);
|
||||
|
||||
public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(tr.Name, Cx.MdReader);
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var t in typeParams.Value)
|
||||
yield return t;
|
||||
}
|
||||
}
|
||||
|
||||
public override Type? ContainingType
|
||||
{
|
||||
get
|
||||
@@ -95,7 +84,11 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => typeParams.Value;
|
||||
public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(tr.Name, Cx.MdReader);
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => GenericsHelper.GetAllTypeParameters(ContainingType, typeParams!.Value);
|
||||
|
||||
public override IEnumerable<Type> ThisGenericArguments => typeParams.Value;
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CIL
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of generic type parameters, including type parameters of
|
||||
/// The list of generic type parameters/arguments, including type parameters/arguments of
|
||||
/// containing types.
|
||||
/// </summary>
|
||||
public abstract IEnumerable<Entities.Type> TypeParameters { get; }
|
||||
|
||||
41
csharp/extractor/Semmle.Extraction.CIL/StringExtensions.cs
Normal file
41
csharp/extractor/Semmle.Extraction.CIL/StringExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction.CIL
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static (string, string) Split(this string self, int index0)
|
||||
{
|
||||
var split = self.Split(new[] { index0 });
|
||||
return (split[0], split[1]);
|
||||
}
|
||||
|
||||
public static (string, string, string) Split(this string self, int index0, int index1)
|
||||
{
|
||||
var split = self.Split(new[] { index0, index1 });
|
||||
return (split[0], split[1], split[2]);
|
||||
}
|
||||
|
||||
public static (string, string, string, string) Split(this string self, int index0, int index1, int index2)
|
||||
{
|
||||
var split = self.Split(new[] { index0, index1, index2 });
|
||||
return (split[0], split[1], split[2], split[4]);
|
||||
}
|
||||
|
||||
private static List<string> Split(this string self, params int[] indices)
|
||||
{
|
||||
var ret = new List<string>();
|
||||
var previousIndex = 0;
|
||||
foreach (var index in indices.OrderBy(i => i))
|
||||
{
|
||||
ret.Add(self.Substring(previousIndex, index - previousIndex));
|
||||
previousIndex = index;
|
||||
}
|
||||
|
||||
ret.Add(self.Substring(previousIndex));
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user