C#: Tidy code for constructing underlying tuple structs

This commit is contained in:
Tom Hvitved
2020-09-14 10:07:28 +02:00
parent 6c5b30d2a4
commit 19746023d9
3 changed files with 34 additions and 25 deletions

View File

@@ -11,11 +11,11 @@ namespace Semmle.Extraction.CSharp.Entities
{
class NamedType : Type<INamedTypeSymbol>
{
NamedType(Context cx, INamedTypeSymbol init, bool fromTuple)
NamedType(Context cx, INamedTypeSymbol init, bool constructUnderlyingTupleType)
: base(cx, init)
{
typeArgumentsLazy = new Lazy<Type[]>(() => symbol.TypeArguments.Select(t => Create(cx, t)).ToArray());
this.fromTuple = fromTuple;
this.constructUnderlyingTupleType = constructUnderlyingTupleType;
}
public static NamedType Create(Context cx, INamedTypeSymbol type) =>
@@ -23,6 +23,12 @@ namespace Semmle.Extraction.CSharp.Entities
? NamedTupleTypeFactory.Instance.CreateEntity(cx, (type, false), (type, false))
: NamedTypeFactory.Instance.CreateEntityFromSymbol(cx, type);
/// <summary>
/// Creates a named type entity from a tuple type. Unlike `Create`, this
/// will create an entity for the underlying `System.ValueTuple` struct.
/// For example, `(int, string)` will result in an entity for
/// `System.ValueTuple<int, string>`.
/// </summary>
public static NamedType CreateNamedTypeFromTupleType(Context cx, INamedTypeSymbol type) =>
NamedTupleTypeFactory.Instance.CreateEntity(cx, (type, true), (type, true));
@@ -58,7 +64,9 @@ namespace Semmle.Extraction.CSharp.Entities
}
else
{
var unbound = fromTuple ? CreateNamedTypeFromTupleType(Context, symbol.ConstructedFrom) : Type.Create(Context, symbol.ConstructedFrom);
var unbound = constructUnderlyingTupleType
? CreateNamedTypeFromTupleType(Context, symbol.ConstructedFrom)
: Type.Create(Context, symbol.ConstructedFrom);
trapFile.constructed_generic(this, unbound.TypeRef);
for (int i = 0; i < symbol.TypeArguments.Length; ++i)
@@ -68,7 +76,7 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
PopulateType(trapFile, getTupleAsTuple: !fromTuple);
PopulateType(trapFile, constructUnderlyingTupleType);
if (symbol.EnumUnderlyingType != null)
{
@@ -84,7 +92,7 @@ namespace Semmle.Extraction.CSharp.Entities
}
readonly Lazy<Type[]> typeArgumentsLazy;
private readonly bool fromTuple;
private readonly bool constructUnderlyingTupleType;
public Type[] TypeArguments => typeArgumentsLazy.Value;
@@ -125,7 +133,7 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.Write('*');
else
{
symbol.BuildTypeId(Context, trapFile, symbol, getTupleAsTuple: !fromTuple);
symbol.BuildTypeId(Context, trapFile, symbol, constructUnderlyingTupleType);
trapFile.Write(";type");
}
}

View File

@@ -47,7 +47,7 @@ namespace Semmle.Extraction.CSharp.Entities
symbol.ContainingType != null && ConstructedOrParentIsConstructed(symbol.ContainingType);
}
static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool getTupleAsTuple)
static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool constructUnderlyingTupleType)
{
switch (t.SpecialType)
{
@@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities
{
case TypeKind.Class: return Kinds.TypeKind.CLASS;
case TypeKind.Struct:
return ((INamedTypeSymbol)t).IsTupleType && getTupleAsTuple
return ((INamedTypeSymbol)t).IsTupleType && !constructUnderlyingTupleType
? Kinds.TypeKind.TUPLE
: Kinds.TypeKind.STRUCT;
case TypeKind.Interface: return Kinds.TypeKind.INTERFACE;
@@ -87,7 +87,7 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
protected void PopulateType(TextWriter trapFile, bool getTupleAsTuple = true)
protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleType = false)
{
PopulateMetadataHandle(trapFile);
PopulateAttributes();
@@ -95,9 +95,9 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.Write("types(");
trapFile.WriteColumn(this);
trapFile.Write(',');
trapFile.WriteColumn((int)GetClassType(Context, symbol, getTupleAsTuple));
trapFile.WriteColumn((int)GetClassType(Context, symbol, constructUnderlyingTupleType));
trapFile.Write(",\"");
symbol.BuildDisplayName(Context, trapFile, getTupleAsTuple);
symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType);
trapFile.WriteLine("\")");
// Visit base types

View File

@@ -152,10 +152,11 @@ namespace Semmle.Extraction.CSharp
/// <param name="cx">The extraction context.</param>
/// <param name="trapFile">The trap builder used to store the result.</param>
/// <param name="symbolBeingDefined">The outer symbol being defined (to avoid recursive ids).</param>
public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool getTupleAsTuple = true) =>
type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, getTupleAsTuple);
/// <param name="constructUnderlyingTupleType">Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types.</param>
public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) =>
type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType);
static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool getTupleAsTuple)
static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType)
{
using (cx.StackGuard)
{
@@ -173,7 +174,7 @@ namespace Semmle.Extraction.CSharp
case TypeKind.Delegate:
case TypeKind.Error:
var named = (INamedTypeSymbol)type;
named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, getTupleAsTuple);
named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
return;
case TypeKind.Pointer:
var ptr = (IPointerTypeSymbol)type;
@@ -195,7 +196,7 @@ namespace Semmle.Extraction.CSharp
}
}
static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool getTupleAsTuple = true)
static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false)
{
// We need to keep track of the symbol being defined in order to avoid cyclic labels.
// For example, in
@@ -214,8 +215,8 @@ namespace Semmle.Extraction.CSharp
if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined))
trapFile.Write("__self__");
else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined))
type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, getTupleAsTuple);
else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && !getTupleAsTuple)
type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType)
trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType));
else
trapFile.WriteSubId(CreateEntity(cx, symbol));
@@ -264,9 +265,9 @@ namespace Semmle.Extraction.CSharp
trapFile.Write("::");
}
static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool getTupleAsTuple)
static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType)
{
if (getTupleAsTuple && named.IsTupleType)
if (!constructUnderlyingTupleType && named.IsTupleType)
{
trapFile.Write('(');
trapFile.BuildList(",", named.TupleElements,
@@ -310,7 +311,7 @@ namespace Semmle.Extraction.CSharp
}
else
{
named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, getTupleAsTuple);
named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
trapFile.Write('<');
// Encode the nullability of the type arguments in the label.
// Type arguments with different nullability can result in
@@ -362,7 +363,7 @@ namespace Semmle.Extraction.CSharp
/// Constructs a display name string for this type symbol.
/// </summary>
/// <param name="trapFile">The trap builder used to store the result.</param>
public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWriter trapFile, bool getTupleAsTuple = true)
public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType = false)
{
using (cx.StackGuard)
{
@@ -386,7 +387,7 @@ namespace Semmle.Extraction.CSharp
case TypeKind.Delegate:
case TypeKind.Error:
var named = (INamedTypeSymbol)type;
named.BuildNamedTypeDisplayName(cx, trapFile, getTupleAsTuple);
named.BuildNamedTypeDisplayName(cx, trapFile, constructUnderlyingTupleType);
return;
case TypeKind.Pointer:
var ptr = (IPointerTypeSymbol)type;
@@ -405,9 +406,9 @@ namespace Semmle.Extraction.CSharp
}
}
public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool getTupleAsTuple)
public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType)
{
if (getTupleAsTuple && namedType.IsTupleType)
if (!constructUnderlyingTupleType && namedType.IsTupleType)
{
trapFile.Write('(');
trapFile.BuildList(",", namedType.TupleElements.Select(f => f.Type),