Skip to content

Commit 70f4efb

Browse files
authored
Merge pull request #7646 from hvitved/csharp/roslyn-tuple-elements-workaround
C#: Workaround Roslyn bug in `INamedTypeSymbol.TupleElements`
2 parents 40c8881 + dacb33d commit 70f4efb

File tree

4 files changed

+56
-15
lines changed

4 files changed

+56
-15
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ protected override void Populate(TextWriter trapFile)
9090
var tts = (TupleTypeSyntax)syntax;
9191
var tt = (TupleType)type;
9292
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);
93-
tts.Elements.Zip(tt.TupleElements, (s, t) => Create(Context, s.Type, this, t.Type)).Enumerate();
93+
foreach (var (s, t) in tts.Elements.Zip(tt.TupleElements, (s, t) => (s, t?.Type)))
94+
{
95+
if (t is not null)
96+
Create(Context, s.Type, this, t);
97+
}
9498
return;
9599
case SyntaxKind.GenericName:
96100
Emit(trapFile, loc ?? syntax.GetLocation(), parent, type);

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs

+9-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private class TupleTypeFactory : CachedEntityFactory<INamedTypeSymbol, TupleType
2424

2525
private TupleType(Context cx, INamedTypeSymbol init) : base(cx, init)
2626
{
27-
tupleElementsLazy = new Lazy<Field[]>(() => Symbol.TupleElements.Select(t => Field.Create(cx, t)).ToArray());
27+
tupleElementsLazy = new Lazy<Field?[]>(() => Symbol.GetTupleElementsMaybeNull().Select(t => t is null ? null : Field.Create(cx, t)).ToArray());
2828
}
2929

3030
// All tuple types are "local types"
@@ -47,7 +47,10 @@ public override void Populate(TextWriter trapFile)
4747

4848
var index = 0;
4949
foreach (var element in TupleElements)
50-
trapFile.tuple_element(this, index++, element);
50+
{
51+
if (element is not null)
52+
trapFile.tuple_element(this, index++, element);
53+
}
5154

5255
// Note: symbol.Locations seems to be very inconsistent
5356
// about what locations are available for a tuple type.
@@ -56,9 +59,10 @@ public override void Populate(TextWriter trapFile)
5659
trapFile.type_location(this, Context.CreateLocation(l));
5760
}
5861

59-
private readonly Lazy<Field[]> tupleElementsLazy;
60-
public Field[] TupleElements => tupleElementsLazy.Value;
62+
private readonly Lazy<Field?[]> tupleElementsLazy;
63+
public Field?[] TupleElements => tupleElementsLazy.Value;
6164

62-
public override IEnumerable<Type> TypeMentions => TupleElements.Select(e => e.Type);
65+
public override IEnumerable<Type> TypeMentions =>
66+
TupleElements.OfType<Field>().Select(e => e.Type);
6367
}
6468
}

csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs

+26-7
Original file line numberDiff line numberDiff line change
@@ -280,17 +280,30 @@ private static void BuildAssembly(IAssemblySymbol asm, EscapingTextWriter trapFi
280280
private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) =>
281281
BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined));
282282

283+
/// <summary>
284+
/// Workaround for a Roslyn bug: https://github.com/dotnet/roslyn/issues/53943
285+
/// </summary>
286+
public static IEnumerable<IFieldSymbol?> GetTupleElementsMaybeNull(this INamedTypeSymbol type) =>
287+
type.TupleElements;
288+
283289
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType)
284290
{
285291
if (!constructUnderlyingTupleType && named.IsTupleType)
286292
{
287293
trapFile.Write('(');
288-
trapFile.BuildList(",", named.TupleElements,
289-
f =>
294+
trapFile.BuildList(",", named.GetTupleElementsMaybeNull(),
295+
(i, f) =>
290296
{
291-
trapFile.Write((f.CorrespondingTupleField ?? f).Name);
292-
trapFile.Write(":");
293-
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
297+
if (f is null)
298+
{
299+
trapFile.Write($"null({i})");
300+
}
301+
else
302+
{
303+
trapFile.Write((f.CorrespondingTupleField ?? f).Name);
304+
trapFile.Write(":");
305+
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
306+
}
294307
}
295308
);
296309
trapFile.Write(")");
@@ -464,8 +477,14 @@ private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, C
464477
trapFile.Write('(');
465478
trapFile.BuildList(
466479
",",
467-
namedType.TupleElements.Select(f => f.Type),
468-
t => t.BuildDisplayName(cx, trapFile));
480+
namedType.GetTupleElementsMaybeNull(),
481+
(i, f) =>
482+
{
483+
if (f is null)
484+
trapFile.Write($"null({i})");
485+
else
486+
f.Type.BuildDisplayName(cx, trapFile);
487+
});
469488
trapFile.Write(")");
470489
return;
471490
}

csharp/extractor/Semmle.Extraction/TrapExtensions.cs

+16-2
Original file line numberDiff line numberDiff line change
@@ -232,19 +232,33 @@ public static TextWriter AppendList<T>(this EscapingTextWriter trapFile, string
232232
/// <param name="items">The list of items.</param>
233233
/// <param name="action">The action on each item.</param>
234234
/// <returns>The original trap builder (fluent interface).</returns>
235-
public static T1 BuildList<T1, T2>(this T1 trapFile, string separator, IEnumerable<T2> items, Action<T2> action)
235+
public static T1 BuildList<T1, T2>(this T1 trapFile, string separator, IEnumerable<T2> items, Action<int, T2> action)
236236
where T1 : TextWriter
237237
{
238238
var first = true;
239+
var i = 0;
239240
foreach (var item in items)
240241
{
241242
if (first)
242243
first = false;
243244
else
244245
trapFile.Write(separator);
245-
action(item);
246+
action(i++, item);
246247
}
247248
return trapFile;
248249
}
250+
251+
/// <summary>
252+
/// Builds a trap builder using a separator and an action for each item in the list.
253+
/// </summary>
254+
/// <typeparam name="T">The type of the items.</typeparam>
255+
/// <param name="trapFile">The trap builder to append to.</param>
256+
/// <param name="separator">The separator string (e.g. ",")</param>
257+
/// <param name="items">The list of items.</param>
258+
/// <param name="action">The action on each item.</param>
259+
/// <returns>The original trap builder (fluent interface).</returns>
260+
public static T1 BuildList<T1, T2>(this T1 trapFile, string separator, IEnumerable<T2> items, Action<T2> action)
261+
where T1 : TextWriter =>
262+
trapFile.BuildList(separator, items, (_, item) => action(item));
249263
}
250264
}

0 commit comments

Comments
 (0)