C#: Remove ITrapBuilder in favour of TextWriter.

This commit is contained in:
Calum Grant
2019-08-08 18:15:30 +01:00
parent aeb38a1757
commit e41e8d6547
44 changed files with 552 additions and 554 deletions

View File

@@ -4,6 +4,7 @@ using Semmle.Extraction.Entities;
using Semmle.Util.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Semmle.Extraction
@@ -53,6 +54,28 @@ namespace Semmle.Extraction
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
}
bool DefiningLabel = false;
public void DefineLabel(IEntity entity, TextWriter trapFile)
{
if (DefiningLabel)
{
PopulateLater(() => DefineLabel(entity, trapFile));
}
else
{
try
{
DefiningLabel = true;
entity.DefineLabel(trapFile);
}
finally
{
DefiningLabel = false;
}
}
}
/// <summary>
/// Creates a new entity using the factory.
/// Uses a different cache to <see cref="CreateEntity{Type, Entity}(ICachedEntityFactory{Type, Entity}, Type)"/>,
@@ -73,23 +96,27 @@ namespace Semmle.Extraction
}
else
{
var id = entity.Id;
#if DEBUG_LABELS
CheckEntityHasUniqueLabel(id, entity);
#endif
label = new Label(GetNewId());
label = GetNewLabel();
entity.Label = label;
entityLabelCache[entity] = label;
DefineLabel(label, id);
DefineLabel(entity, TrapWriter.Writer);
if (entity.NeedsPopulation)
Populate(init as ISymbol, entity);
#if DEBUG_LABELS
var id = new StringWriter();
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
#endif
}
return entity;
}
}
#if DEBUG_LABELS
private void CheckEntityHasUniqueLabel(IId id, ICachedEntity entity)
private void CheckEntityHasUniqueLabel(string id, ICachedEntity entity)
{
if (idLabelCache.TryGetValue(id, out var originalEntity))
{
@@ -102,6 +129,8 @@ namespace Semmle.Extraction
}
#endif
public Label GetNewLabel() => new Label(GetNewId());
private Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
{
if (objectEntityCache.TryGetValue(init, out var cached))
@@ -109,22 +138,22 @@ namespace Semmle.Extraction
using (StackGuard)
{
var label = new Label(GetNewId());
var label = GetNewLabel();
var entity = factory.Create(this, init);
entity.Label = label;
objectEntityCache[init] = entity;
var id = entity.Id;
DefineLabel(label, id);
#if DEBUG_LABELS
CheckEntityHasUniqueLabel(id, entity);
#endif
DefineLabel(entity, TrapWriter.Writer);
if (entity.NeedsPopulation)
Populate(init as ISymbol, entity);
#if DEBUG_LABELS
var id = new StringWriter();
entity.WriteId(id);
CheckEntityHasUniqueLabel(id.ToString(), entity);
#endif
return entity;
}
}
@@ -158,29 +187,17 @@ namespace Semmle.Extraction
/// </summary>
public void AddFreshLabel(IEntity entity)
{
var label = new Label(GetNewId());
TrapWriter.Emit(new DefineFreshLabelEmitter(label));
entity.Label = label;
entity.Label = GetNewLabel();
entity.DefineFreshLabel(TrapWriter.Writer);
}
#if DEBUG_LABELS
readonly Dictionary<IId, ICachedEntity> idLabelCache = new Dictionary<IId, ICachedEntity>();
readonly Dictionary<string, ICachedEntity> idLabelCache = new Dictionary<string, ICachedEntity>();
#endif
readonly Dictionary<object, ICachedEntity> objectEntityCache = new Dictionary<object, ICachedEntity>();
readonly Dictionary<ICachedEntity, Label> entityLabelCache = new Dictionary<ICachedEntity, Label>();
readonly HashSet<Label> extractedGenerics = new HashSet<Label>();
public void DefineLabel(IEntity entity)
{
entity.Label = new Label(GetNewId());
DefineLabel(entity.Label, entity.Id);
}
void DefineLabel(Label label, IId id)
{
TrapWriter.Emit(new DefineLabelEmitter(label, id));
}
/// <summary>
/// Queue of items to populate later.
/// The only reason for this is so that the call stack does not
@@ -300,43 +317,6 @@ namespace Semmle.Extraction
}
}
class DefineLabelEmitter : ITrapEmitter
{
readonly Label label;
readonly IId id;
public DefineLabelEmitter(Label label, IId id)
{
this.label = label;
this.id = id;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
{
label.AppendTo(tb);
tb.Append("=");
id.AppendTo(tb);
tb.AppendLine();
}
}
class DefineFreshLabelEmitter : ITrapEmitter
{
readonly Label Label;
public DefineFreshLabelEmitter(Label label)
{
Label = label;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
{
Label.AppendTo(tb);
tb.Append("=*");
tb.AppendLine();
}
}
class PushEmitter : ITrapEmitter
{
readonly Key Key;
@@ -346,20 +326,19 @@ namespace Semmle.Extraction
Key = key;
}
public void EmitToTrapBuilder(ITrapBuilder tb)
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(".push ");
Key.AppendTo(tb);
tb.AppendLine();
tw.Write(".push ");
Key.AppendTo(tw);
tw.WriteLine();
}
}
class PopEmitter : ITrapEmitter
{
public void EmitToTrapBuilder(ITrapBuilder tb)
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(".pop");
tb.AppendLine();
tw.WriteLine(".pop");
}
}
@@ -373,6 +352,13 @@ namespace Semmle.Extraction
/// <exception cref="InternalError">Thrown on invalid trap stack behaviour.</exception>
public void Populate(ISymbol optionalSymbol, ICachedEntity entity)
{
if (DefiningLabel)
{
// Don't write tuples etc if we're currently defining a label
PopulateLater(() => Populate(optionalSymbol, entity));
return;
}
bool duplicationGuard;
bool deferred;

View File

@@ -65,14 +65,15 @@ namespace Semmle.Extraction.Entities
return AssemblyConstructorFactory.Instance.CreateEntity(cx, null);
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
trapFile.Write(assembly.ToString());
if (assemblyPath is null)
{
return assemblyPath == null
? new Key(assembly, ";assembly")
: new Key(assembly, "#file:///", assemblyPath.Replace("\\", "/"), ";assembly");
trapFile.Write("#file:///");
trapFile.Write(assemblyPath.Replace("\\", "/"));
}
trapFile.Write(";assembly");
}
}
}

View File

@@ -63,13 +63,14 @@ namespace Semmle.Extraction.Entities
}
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
if (Path is null)
trapFile.Write("GENERATED;sourcefile");
else
{
return Path == null ?
new Key("GENERATED;sourcefile") :
new Key(DatabasePath, ";sourcefile");
trapFile.Write(DatabasePath);
trapFile.Write(";sourcefile");
}
}
@@ -104,7 +105,10 @@ namespace Semmle.Extraction.Entities
Context.TrapWriter.files(this, "", "", "");
}
public override IId Id => new Key("GENERATED;sourcefile");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write("GENERATED;sourcefile");
}
public static GeneratedFile Create(Context cx) =>
GeneratedFileFactory.Instance.CreateEntity(cx, null);

View File

@@ -36,7 +36,11 @@ namespace Semmle.Extraction.Entities
public override bool NeedsPopulation => true;
public override IId Id => new Key(DatabasePath, ";folder");
public override void WriteId(System.IO.TextWriter trapFile)
{
trapFile.Write(DatabasePath);
trapFile.Write(";folder");
}
public static Folder Create(Context cx, DirectoryInfo folder) =>
FolderFactory.Instance.CreateEntity2(cx, folder);

View File

@@ -1,3 +1,5 @@
using System.IO;
namespace Semmle.Extraction.Entities
{
public class GeneratedLocation : SourceLocation
@@ -15,7 +17,12 @@ namespace Semmle.Extraction.Entities
Context.TrapWriter.locations_default(this, GeneratedFile, 0, 0, 0, 0);
}
public override IId Id => new Key("loc,", GeneratedFile, ",0,0,0,0");
public override void WriteId(TextWriter trapFile)
{
trapFile.Write("loc,");
trapFile.WriteSubId(GeneratedFile);
trapFile.Write(",0,0,0,0");
}
public override int GetHashCode() => 98732567;

View File

@@ -1,3 +1,4 @@
using System.IO;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction.Entities
@@ -31,15 +32,20 @@ namespace Semmle.Extraction.Entities
private set;
}
public override IId Id
public override void WriteId(System.IO.TextWriter trapFile)
{
get
{
FileLinePositionSpan l = symbol.GetLineSpan();
FileEntity = Entities.File.Create(Context, l.Path);
return new Key("loc,", FileEntity, ",", l.Span.Start.Line + 1, ",",
l.Span.Start.Character + 1, ",", l.Span.End.Line + 1, ",", l.Span.End.Character);
}
FileLinePositionSpan l = symbol.GetLineSpan();
FileEntity = Entities.File.Create(Context, l.Path);
trapFile.Write("loc,");
trapFile.WriteSubId(FileEntity);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.Start.Character + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Line + 1);
trapFile.Write(',');
trapFile.Write(l.Span.End.Character);
}
class SourceLocationFactory : ICachedEntityFactory<Microsoft.CodeAnalysis.Location, SourceLocation>

View File

@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
using System;
using System.IO;
namespace Semmle.Extraction
{
@@ -26,10 +26,17 @@ namespace Semmle.Extraction
Label Label { set; get; }
/// <summary>
/// The ID used for the entity, as it is in the trap file.
/// Could be '*'.
/// Writes the unique identifier of this entitiy to a trap file.
/// </summary>
IId Id { get; }
/// <param name="writer"></param>
void WriteId(TextWriter writer);
/// <summary>
/// Writes the quoted identifier of this entity,
/// which could be @"..." or *
/// </summary>
/// <param name="writer"></param>
void WriteQuotedId(TextWriter writer);
/// <summary>
/// The location for reporting purposes.
@@ -133,5 +140,34 @@ namespace Semmle.Extraction
/// <returns>The entity.</returns>
public static Entity CreateEntity2<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity2(factory, init);
public static void DefineLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.Write("=");
entity.WriteQuotedId(trapFile);
trapFile.WriteLine();
}
public static void DefineFreshLabel(this IEntity entity, TextWriter trapFile)
{
trapFile.WriteLabel(entity);
trapFile.WriteLine("=*");
}
/// <summary>
/// Generates a debug string for this entity.
/// </summary>
/// <param name="entity">The entity to view.</param>
/// <returns>The debug string.</returns>
public static string GetDebugLabel(this IEntity entity)
{
var writer = new StringWriter();
writer.WriteLabel(entity.Label.Value);
writer.Write('=');
entity.WriteQuotedId(writer);
return writer.ToString();
}
}
}

View File

@@ -1,4 +1,5 @@
using Semmle.Extraction.Entities;
using System.IO;
namespace Semmle.Extraction
{
@@ -20,6 +21,16 @@ namespace Semmle.Extraction
get; set;
}
public void WriteId(TextWriter writer)
{
writer.Write('*');
}
public void WriteQuotedId(TextWriter writer)
{
WriteId(writer);
}
public override string ToString() => Label.ToString();
public IId Id => FreshId.Instance;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Semmle.Extraction
@@ -16,7 +17,7 @@ namespace Semmle.Extraction
/// <summary>
/// Appends this ID to the supplied trap builder.
/// </summary>
void AppendTo(ITrapBuilder tb);
void AppendTo(TextWriter tw);
}
/// <summary>
@@ -37,9 +38,9 @@ namespace Semmle.Extraction
public override int GetHashCode() => 0;
public void AppendTo(ITrapBuilder tb)
public void AppendTo(System.IO.TextWriter tw)
{
tb.Append("*");
tw.Write('*');
}
}
@@ -49,34 +50,41 @@ namespace Semmle.Extraction
/// </summary>
public class Key : IId
{
readonly IdTrapBuilder TrapBuilder;
readonly StringWriter TrapBuilder = new StringWriter();
/// <summary>
/// Creates a new key by concatenating the contents of the supplied arguments.
/// </summary>
public Key(params object[] args)
{
TrapBuilder = new IdTrapBuilder();
TrapBuilder = new StringWriter();
foreach (var arg in args)
TrapBuilder.Append(arg);
{
if (arg is IEntity)
{
var key = ((IEntity)arg).Label;
TrapBuilder.Write("{#");
TrapBuilder.Write(key.Value.ToString());
TrapBuilder.Write("}");
}
else
TrapBuilder.Write(arg.ToString());
}
}
/// <summary>
/// Creates a new key by applying the supplied action to an empty
/// trap builder.
/// </summary>
public Key(Action<ITrapBuilder> action)
public Key(Action<TextWriter> action)
{
TrapBuilder = new IdTrapBuilder();
action(TrapBuilder);
}
public override string ToString()
{
// Only implemented for debugging purposes
var tsb = new TrapStringBuilder();
AppendTo(tsb);
return tsb.ToString();
return TrapBuilder.ToString();
}
public override bool Equals(object obj)
@@ -84,37 +92,23 @@ namespace Semmle.Extraction
if (obj.GetType() != GetType())
return false;
var id = (Key)obj;
return id.TrapBuilder.Fragments.SequenceEqual(TrapBuilder.Fragments);
return TrapBuilder.ToString() == id.TrapBuilder.ToString();
}
public override int GetHashCode()
public override int GetHashCode() => TrapBuilder.ToString().GetHashCode();
public void AppendTo(TextWriter tb)
{
unchecked
{
int hash = 17;
foreach (var fragment in TrapBuilder.Fragments)
{
hash = hash * 23 + fragment.GetHashCode();
}
return hash;
}
tb.Write("@\"");
tb.Write(TrapBuilder.ToString());
tb.Write("\"");
}
public void AppendTo(ITrapBuilder tb)
{
tb.Append("@\"");
foreach (var fragment in TrapBuilder.Fragments)
tb.Append(fragment);
tb.Append("\"");
}
class IdTrapBuilder : ITrapBuilder
class IdTrapBuilder
{
readonly public List<string> Fragments = new List<string>();
public ITrapBuilder Append(object arg)
public void Append(object arg)
{
if (arg is IEntity)
{
@@ -125,17 +119,14 @@ namespace Semmle.Extraction
}
else
Fragments.Add(arg.ToString());
return this;
}
public ITrapBuilder Append(string arg)
public void Append(string arg)
{
Fragments.Add(arg);
return this;
}
public ITrapBuilder AppendLine()
public void AppendLine()
{
throw new NotImplementedException();
}
@@ -145,7 +136,7 @@ namespace Semmle.Extraction
/// <summary>
/// A label referencing an entity, of the form "#123".
/// </summary>
public struct Label : IId
public struct Label
{
public Label(int value) : this()
{
@@ -181,12 +172,13 @@ namespace Semmle.Extraction
/// Constructs a unique string for this label.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
public void AppendTo(ITrapBuilder tb)
public void AppendTo(System.IO.TextWriter tw)
{
if (!Valid)
throw new NullReferenceException("Attempt to use an invalid label");
tb.Append("#").Append(Value);
tw.Write('#');
tw.Write(Value);
}
}
}

View File

@@ -1,3 +1,5 @@
using System.IO;
namespace Semmle.Extraction
{
/// <summary>
@@ -34,9 +36,13 @@ namespace Semmle.Extraction
public Initializer UnderlyingObject => symbol;
public abstract IId Id
public abstract void WriteId(System.IO.TextWriter trapFile);
public void WriteQuotedId(TextWriter trapFile)
{
get;
trapFile.Write("@\"");
WriteId(trapFile);
trapFile.Write('\"');
}
public abstract bool NeedsPopulation

View File

@@ -1,35 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Semmle.Extraction
{
/// <summary>
/// A trap builder.
///
/// A trap builder is used to construct a string that is to be
/// persisted in a trap file (similar to how a <see cref="StringBuilder"/>
/// can be used to construct a string).
/// </summary>
public interface ITrapBuilder
{
/// <summary>
/// Append the given object to this trap builder.
/// </summary>
ITrapBuilder Append(object arg);
/// <summary>
/// Append the given string to this trap builder.
/// </summary>
ITrapBuilder Append(string arg);
/// <summary>
/// Append a newline to this trap builder.
/// </summary>
ITrapBuilder AppendLine();
}
public static class ITrapBuilderExtensions
public static class TrapBuilderExtensions
{
/// <summary>
/// Appends a [comma] separated list to a trap builder.
@@ -39,9 +15,9 @@ namespace Semmle.Extraction
/// <param name="separator">The separator string (e.g. ",")</param>
/// <param name="items">The list of items.</param>
/// <returns>The original trap builder (fluent interface).</returns>
public static ITrapBuilder AppendList<T>(this ITrapBuilder tb, string separator, IEnumerable<T> items)
public static TextWriter AppendList<T>(this TextWriter tb, string separator, IEnumerable<T> items) where T:IEntity
{
return tb.BuildList(separator, items, (x, tb0) => { tb0.Append(x); });
return tb.BuildList(separator, items, (x, tb0) => { tb0.WriteSubId(x); });
}
/// <summary>
@@ -53,44 +29,15 @@ namespace Semmle.Extraction
/// <param name="items">The list of items.</param>
/// <param name="action">The action on each item.</param>
/// <returns>The original trap builder (fluent interface).</returns>
public static ITrapBuilder BuildList<T>(this ITrapBuilder tb, string separator, IEnumerable<T> items, Action<T, ITrapBuilder> action)
public static TextWriter BuildList<T>(this TextWriter tb, string separator, IEnumerable<T> items, Action<T, TextWriter> action)
{
bool first = true;
foreach (var item in items)
{
if (first) first = false; else tb.Append(separator);
if (first) first = false; else tb.Write(separator);
action(item, tb);
}
return tb;
}
}
/// <summary>
/// A <see cref="StringBuilder"/> implementation of <see cref="ITrapBuilder"/>,
/// used for debugging only.
/// </summary>
public class TrapStringBuilder : ITrapBuilder
{
readonly StringBuilder StringBuilder = new StringBuilder();
public ITrapBuilder Append(object arg)
{
StringBuilder.Append(arg);
return this;
}
public ITrapBuilder Append(string arg)
{
StringBuilder.Append(arg);
return this;
}
public ITrapBuilder AppendLine()
{
StringBuilder.AppendLine();
return this;
}
public override string ToString() => StringBuilder.ToString();
}
}

View File

@@ -10,7 +10,7 @@ namespace Semmle.Extraction
{
public interface ITrapEmitter
{
void EmitToTrapBuilder(ITrapBuilder tb);
void EmitToTrapBuilder(TextWriter tw);
}
public sealed class TrapWriter : IDisposable
@@ -35,9 +35,6 @@ namespace Semmle.Extraction
public StreamWriter Writer => WriterLazy.Value;
readonly Lazy<TrapBuilder> BuilderLazy;
TrapBuilder Builder => BuilderLazy.Value;
readonly ILogger Logger;
public TrapWriter(ILogger logger, string outputfile, string trap, string archive, bool discardDuplicates)
@@ -68,7 +65,6 @@ namespace Semmle.Extraction
var compressionStream = new GZipStream(fileStream, CompressionMode.Compress);
return new StreamWriter(compressionStream, UTF8, 2000000);
});
BuilderLazy = new Lazy<TrapBuilder>(() => new TrapBuilder(WriterLazy.Value));
this.archive = archive;
this.discardDuplicates = discardDuplicates;
}
@@ -177,7 +173,7 @@ namespace Semmle.Extraction
public void Emit(ITrapEmitter emitter)
{
emitter.EmitToTrapBuilder(Builder);
emitter.EmitToTrapBuilder(Writer);
}
/// <summary>
@@ -196,34 +192,6 @@ namespace Semmle.Extraction
}
}
class TrapBuilder : ITrapBuilder
{
readonly StreamWriter StreamWriter;
public TrapBuilder(StreamWriter sw)
{
StreamWriter = sw;
}
public ITrapBuilder Append(object arg)
{
StreamWriter.Write(arg);
return this;
}
public ITrapBuilder Append(string arg)
{
StreamWriter.Write(arg);
return this;
}
public ITrapBuilder AppendLine()
{
StreamWriter.WriteLine();
return this;
}
}
/// <summary>
/// Attempts to archive the specified input file to the normal area of the source archive.
/// The file's path must be sufficiently short so as to render the path of its copy in the

View File

@@ -1,3 +1,4 @@
using System.IO;
using System.Linq;
namespace Semmle.Extraction
@@ -35,7 +36,7 @@ namespace Semmle.Extraction
array.Sum(encoding.GetByteCount) > maxStringBytes;
}
private static void WriteString(ITrapBuilder tb, string s) => tb.Append(EncodeString(s));
private static void WriteString(TextWriter tb, string s) => tb.Write(EncodeString(s));
/// <summary>
/// Truncates a string such that the output UTF8 does not exceed <paramref name="bytesRemaining"/> bytes.
@@ -73,7 +74,7 @@ namespace Semmle.Extraction
/// <param name="tb">The trapbuilder</param>
/// <param name="s">The string to output.</param>
/// <param name="bytesRemaining">The remaining bytes available to output.</param>
private static void WriteTruncatedString(ITrapBuilder tb, string s, ref int bytesRemaining)
private static void WriteTruncatedString(TextWriter tb, string s, ref int bytesRemaining)
{
WriteString(tb, TruncateString(s, ref bytesRemaining));
}
@@ -81,63 +82,64 @@ namespace Semmle.Extraction
/// <summary>
/// Constructs a unique string for this tuple.
/// </summary>
/// <param name="tb">The trap builder used to store the result.</param>
public void EmitToTrapBuilder(ITrapBuilder tb)
/// <param name="tw">The trap builder used to store the result.</param>
public void EmitToTrapBuilder(TextWriter tw)
{
tb.Append(Name).Append("(");
tw.Write(Name);
tw.Write("(");
int column = 0;
foreach (var a in Args)
{
if (column > 0) tb.Append(", ");
tw.WriteSeparator(", ", column++);
switch(a)
{
case Label l:
l.AppendTo(tb);
l.AppendTo(tw);
break;
case IEntity e:
e.Label.AppendTo(tb);
e.Label.AppendTo(tw);
break;
case string s:
tb.Append("\"");
tw.Write("\"");
if (NeedsTruncation(s))
{
// Slow path
int remaining = maxStringBytes;
WriteTruncatedString(tb, s, ref remaining);
WriteTruncatedString(tw, s, ref remaining);
}
else
{
// Fast path
WriteString(tb, s);
WriteString(tw, s);
}
tb.Append("\"");
tw.Write("\"");
break;
case System.Enum _:
tb.Append((int)a);
tw.Write((int)a);
break;
case int i:
tb.Append(i);
tw.Write(i);
break;
case float f:
tb.Append(f.ToString("0.#####e0")); // Trap importer won't accept ints
tw.Write(f.ToString("0.#####e0")); // Trap importer won't accept ints
break;
case string[] array:
tb.Append("\"");
tw.Write('\"');
if (NeedsTruncation(array))
{
// Slow path
int remaining = maxStringBytes;
foreach (var element in array)
WriteTruncatedString(tb, element, ref remaining);
WriteTruncatedString(tw, element, ref remaining);
}
else
{
// Fast path
foreach (var element in array)
WriteString(tb, element);
WriteString(tw, element);
}
tb.Append("\"");
tw.Write('\"');
break;
case null:
throw new InternalError($"Attempt to write a null argument tuple {Name} at column {column}");
@@ -147,14 +149,13 @@ namespace Semmle.Extraction
++column;
}
tb.Append(")");
tb.AppendLine();
tw.WriteLine(")");
}
public override string ToString()
{
// Only implemented for debugging purposes
var tsb = new TrapStringBuilder();
var tsb = new StringWriter();
EmitToTrapBuilder(tsb);
return tsb.ToString();
}