mirror of
https://github.com/github/codeql.git
synced 2026-05-05 05:35:13 +02:00
Share entity base classes between CIL and source extraction
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// A cached entity.
|
||||
///
|
||||
/// The <see cref="Entity.Id"/> property is used as label in caching.
|
||||
/// </summary>
|
||||
public abstract class CachedEntity : LabelledEntity
|
||||
{
|
||||
protected CachedEntity(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the <see cref="Label"/> field and generates output in the trap file
|
||||
/// as required. Is only called when <see cref="NeedsPopulation"/> returns
|
||||
/// <code>true</code> and the entity has not already been populated.
|
||||
/// </summary>
|
||||
public abstract void Populate(TextWriter trapFile);
|
||||
|
||||
public abstract bool NeedsPopulation { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An abstract symbol, which encapsulates a data type (such as a C# symbol).
|
||||
/// </summary>
|
||||
/// <typeparam name="TSymbol">The type of the symbol.</typeparam>
|
||||
public abstract class CachedEntity<TSymbol> : CachedEntity
|
||||
{
|
||||
protected CachedEntity(Context context, TSymbol symbol) : base(context)
|
||||
{
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For debugging.
|
||||
/// </summary>
|
||||
public string DebugContents
|
||||
{
|
||||
get
|
||||
{
|
||||
using var trap = new StringWriter();
|
||||
Populate(trap);
|
||||
return trap.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public TSymbol symbol
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
//object? ICachedEntity.UnderlyingObject => symbol;
|
||||
|
||||
public TSymbol UnderlyingObject => symbol;
|
||||
|
||||
public override bool NeedsPopulation { get; }
|
||||
|
||||
public override int GetHashCode() => symbol is null ? 0 : symbol.GetHashCode();
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
var other = obj as CachedEntity<TSymbol>;
|
||||
return other?.GetType() == GetType() && Equals(other.symbol, symbol);
|
||||
}
|
||||
|
||||
public override TrapStackBehaviour TrapStackBehaviour { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class used to wrap an `ISymbol` object, which uses `SymbolEqualityComparer.Default`
|
||||
/// for comparison.
|
||||
/// </summary>
|
||||
public struct SymbolEqualityWrapper
|
||||
{
|
||||
public ISymbol Symbol { get; }
|
||||
|
||||
public SymbolEqualityWrapper(ISymbol symbol) { Symbol = symbol; }
|
||||
|
||||
public override bool Equals(object? other) =>
|
||||
other is SymbolEqualityWrapper sew && SymbolEqualityComparer.Default.Equals(Symbol, sew.Symbol);
|
||||
|
||||
public override int GetHashCode() => 11 * SymbolEqualityComparer.Default.GetHashCode(Symbol);
|
||||
}
|
||||
}
|
||||
64
csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs
Normal file
64
csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
public abstract class Entity : IEntity
|
||||
{
|
||||
protected Context Context { get; }
|
||||
|
||||
protected Entity(Context context)
|
||||
{
|
||||
this.Context = context;
|
||||
}
|
||||
|
||||
public Label Label { get; set; }
|
||||
|
||||
public abstract void WriteId(TextWriter trapFile);
|
||||
|
||||
public abstract void WriteQuotedId(TextWriter trapFile);
|
||||
|
||||
public abstract Location? ReportingLocation { get; }
|
||||
|
||||
public abstract TrapStackBehaviour TrapStackBehaviour { get; }
|
||||
|
||||
public void DefineLabel(TextWriter trapFile, Extractor extractor)
|
||||
{
|
||||
trapFile.WriteLabel(this);
|
||||
trapFile.Write("=");
|
||||
try
|
||||
{
|
||||
WriteQuotedId(trapFile);
|
||||
}
|
||||
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
|
||||
{
|
||||
trapFile.WriteLine("\"");
|
||||
extractor.Message(new Message($"Unhandled exception generating id: {ex.Message}", ToString() ?? "", null, ex.StackTrace));
|
||||
}
|
||||
trapFile.WriteLine();
|
||||
}
|
||||
|
||||
public void DefineFreshLabel(TextWriter trapFile)
|
||||
{
|
||||
trapFile.WriteLabel(this);
|
||||
trapFile.WriteLine("=*");
|
||||
}
|
||||
|
||||
#if DEBUG_LABELS
|
||||
/// <summary>
|
||||
/// Generates a debug string for this entity.
|
||||
/// </summary>
|
||||
public string GetDebugLabel()
|
||||
{
|
||||
using var writer = new StringWriter();
|
||||
writer.WriteLabel(Label.Value);
|
||||
writer.Write('=');
|
||||
WriteQuotedId(writer);
|
||||
return writer.ToString();
|
||||
}
|
||||
#endif
|
||||
|
||||
public override string ToString() => Label.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// An entity which has a default "*" ID assigned to it.
|
||||
/// </summary>
|
||||
public abstract class FreshEntity : UnlabelledEntity
|
||||
{
|
||||
protected FreshEntity(Context cx) : base(cx)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract void Populate(TextWriter trapFile);
|
||||
|
||||
protected void TryPopulate()
|
||||
{
|
||||
Context.Try(null, null, () => Populate(Context.TrapWriter.Writer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For debugging.
|
||||
/// </summary>
|
||||
public string DebugContents
|
||||
{
|
||||
get
|
||||
{
|
||||
using var writer = new StringWriter();
|
||||
Populate(writer);
|
||||
return writer.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
|
||||
|
||||
public override TrapStackBehaviour TrapStackBehaviour { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory for creating cached entities.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInit">The type of the initializer.</typeparam>
|
||||
public interface ICachedEntityFactory<in TInit, out TEntity> where TEntity : CachedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the entity, but does not generate any trap code.
|
||||
/// </summary>
|
||||
TEntity Create(Context cx, TInit init);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
public static class ICachedEntityFactoryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates and populates a new entity, or returns the existing one from the cache,
|
||||
/// based on the supplied cache key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInit">The type used to construct the entity.</typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity to create.</typeparam>
|
||||
/// <param name="factory">The factory used to construct the entity.</param>
|
||||
/// <param name="cx">The extractor context.</param>
|
||||
/// <param name="cacheKey">The key used for caching.</param>
|
||||
/// <param name="init">The initializer for the entity.</param>
|
||||
/// <returns>The entity.</returns>
|
||||
public static TEntity CreateEntity<TInit, TEntity>(this ICachedEntityFactory<TInit, TEntity> factory, Context cx, object cacheKey, TInit init)
|
||||
where TEntity : CachedEntity => cx.CreateEntity(factory, cacheKey, init);
|
||||
|
||||
/// <summary>
|
||||
/// Creates and populates a new entity from an `ISymbol`, or returns the existing one
|
||||
/// from the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSymbol">The type used to construct the entity.</typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity to create.</typeparam>
|
||||
/// <param name="factory">The factory used to construct the entity.</param>
|
||||
/// <param name="cx">The extractor context.</param>
|
||||
/// <param name="init">The initializer for the entity.</param>
|
||||
/// <returns>The entity.</returns>
|
||||
public static TEntity CreateEntityFromSymbol<TSymbol, TEntity>(this ICachedEntityFactory<TSymbol, TEntity> factory, Context cx, TSymbol init)
|
||||
where TSymbol : ISymbol
|
||||
where TEntity : CachedEntity => cx.CreateEntityFromSymbol(factory, init);
|
||||
}
|
||||
}
|
||||
53
csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs
Normal file
53
csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <see cref="FreshEntity"/> directly, and they
|
||||
/// (may) emit contents to the trap file during object construction.
|
||||
///
|
||||
/// Cached entities implement <see cref="CachedEntity"/>, and they
|
||||
/// emit contents to the trap file when <see cref="CachedEntity.Populate"/>
|
||||
/// is called. Caching prevents <see cref="CachedEntity.Populate"/>
|
||||
/// from being called on entities that have already been emitted.
|
||||
/// </summary>
|
||||
public interface IEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// The label of the entity, as it is in the trap file.
|
||||
/// For example, "#123".
|
||||
/// </summary>
|
||||
Label Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Writes the unique identifier of this entitiy to a trap file.
|
||||
/// </summary>
|
||||
/// <param name="trapFile">The trapfile to write to.</param>
|
||||
void WriteId(TextWriter trapFile);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the quoted identifier of this entity,
|
||||
/// which could be @"..." or *
|
||||
/// </summary>
|
||||
/// <param name="trapFile">The trapfile to write to.</param>
|
||||
void WriteQuotedId(TextWriter trapFile);
|
||||
|
||||
/// <summary>
|
||||
/// The location for reporting purposes.
|
||||
/// </summary>
|
||||
Location? ReportingLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// How the entity handles .push and .pop.
|
||||
/// </summary>
|
||||
TrapStackBehaviour TrapStackBehaviour { get; }
|
||||
|
||||
void DefineLabel(TextWriter trapFile, Extractor extractor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
public abstract class LabelledEntity : Entity
|
||||
{
|
||||
protected LabelledEntity(Context cx) : base(cx)
|
||||
{
|
||||
}
|
||||
|
||||
public override void WriteQuotedId(TextWriter trapFile)
|
||||
{
|
||||
trapFile.Write("@\"");
|
||||
WriteId(trapFile);
|
||||
trapFile.Write('\"');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
public abstract class UnlabelledEntity : Entity
|
||||
{
|
||||
protected UnlabelledEntity(Context cx) : base(cx)
|
||||
{
|
||||
cx.AddFreshLabel(this);
|
||||
}
|
||||
|
||||
public sealed override void WriteId(TextWriter writer)
|
||||
{
|
||||
writer.Write('*');
|
||||
}
|
||||
|
||||
public sealed override void WriteQuotedId(TextWriter writer)
|
||||
{
|
||||
WriteId(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user