using Microsoft.CodeAnalysis; using System; using System.IO; namespace Semmle.Extraction { /// /// Any program entity which has a corresponding label in the trap file. /// /// Entities are divided into two types: normal entities and cached /// entities. /// /// Normal entities implement directly, and they /// (may) emit contents to the trap file during object construction. /// /// Cached entities implement , and they /// emit contents to the trap file when /// is called. Caching prevents /// from being called on entities that have already been emitted. /// public interface IEntity { /// /// The label of the entity, as it is in the trap file. /// For example, "#123". /// Label Label { set; get; } /// /// Writes the unique identifier of this entitiy to a trap file. /// /// The trapfile to write to. void WriteId(TextWriter trapFile); /// /// Writes the quoted identifier of this entity, /// which could be @"..." or * /// /// The trapfile to write to. void WriteQuotedId(TextWriter trapFile); /// /// The location for reporting purposes. /// Location? ReportingLocation { get; } /// /// How the entity handles .push and .pop. /// TrapStackBehaviour TrapStackBehaviour { get; } } /// /// How an entity behaves with respect to .push and .pop /// public enum TrapStackBehaviour { /// /// The entity must not be extracted inside a .push/.pop /// NoLabel, /// /// The entity defines its own label, creating a .push/.pop /// PushesLabel, /// /// The entity must be extracted inside a .push/.pop /// NeedsLabel, /// /// The entity can be extracted inside or outside of a .push/.pop /// OptionalLabel } /// /// A cached entity. /// /// The property is used as label in caching. /// public interface ICachedEntity : IEntity { /// /// Populates the field and generates output in the trap file /// as required. Is only called when returns /// true and the entity has not already been populated. /// void Populate(TextWriter trapFile); bool NeedsPopulation { get; } object? UnderlyingObject { get; } } /// /// A factory for creating cached entities. /// /// The type of the initializer. public interface ICachedEntityFactory where TEntity : ICachedEntity { /// /// Initializes the entity, but does not generate any trap code. /// TEntity Create(Context cx, TInit init); } public static class ICachedEntityFactoryExtensions { /// /// Creates and populates a new entity, or returns the existing one from the cache, /// based on the supplied cache key. /// /// The type used to construct the entity. /// The type of the entity to create. /// The factory used to construct the entity. /// The extractor context. /// The key used for caching. /// The initializer for the entity. /// The entity. public static TEntity CreateEntity(this ICachedEntityFactory factory, Context cx, object cacheKey, TInit init) where TEntity : ICachedEntity => cx.CreateEntity(factory, cacheKey, init); /// /// Creates and populates a new entity from an `ISymbol`, or returns the existing one /// from the cache. /// /// The type used to construct the entity. /// The type of the entity to create. /// The factory used to construct the entity. /// The extractor context. /// The initializer for the entity. /// The entity. public static TEntity CreateEntityFromSymbol(this ICachedEntityFactory factory, Context cx, TSymbol init) where TSymbol : ISymbol where TEntity : ICachedEntity => cx.CreateEntityFromSymbol(factory, init); public static void DefineLabel(this IEntity entity, TextWriter trapFile, IExtractor extractor) { trapFile.WriteLabel(entity); trapFile.Write("="); try { entity.WriteQuotedId(trapFile); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { trapFile.WriteLine("\""); extractor.Message(new Message($"Unhandled exception generating id: {ex.Message}", entity.ToString() ?? "", null, ex.StackTrace)); } trapFile.WriteLine(); } public static void DefineFreshLabel(this IEntity entity, TextWriter trapFile) { trapFile.WriteLabel(entity); trapFile.WriteLine("=*"); } /// /// Generates a debug string for this entity. /// /// The entity to view. /// The debug string. public static string GetDebugLabel(this IEntity entity) { using var writer = new StringWriter(); writer.WriteLabel(entity.Label.Value); writer.Write('='); entity.WriteQuotedId(writer); return writer.ToString(); } } }