C#: Take nullability into account when creating symbol entities. Otherwise, an entity with the wrong (cached) nullability could be created.

This commit is contained in:
Calum Grant
2020-04-20 09:32:35 +01:00
parent 4c7d413fa4
commit ead916702a
23 changed files with 70 additions and 24 deletions

View File

@@ -51,7 +51,18 @@ namespace Semmle.Extraction
/// <returns>The new/existing entity.</returns>
public Entity CreateEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
{
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init);
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init, objectEntityCache);
}
/// <summary>
/// Creates a new entity using the factory.
/// </summary>
/// <param name="factory">The entity factory.</param>
/// <param name="init">The initializer for the entity.</param>
/// <returns>The new/existing entity.</returns>
public Entity CreateEntityFromSymbol<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
{
return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init, symbolEntityCache);
}
// A recursion guard against writing to the trap file whilst writing an id to the trap file.
@@ -136,8 +147,12 @@ namespace Semmle.Extraction
public Label GetNewLabel() => new Label(GetNewId());
private Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init) where Entity : ICachedEntity
=> CreateNonNullEntity(factory, init, objectEntityCache);
private Entity CreateNonNullEntity<Type, Entity>(ICachedEntityFactory<Type, Entity> factory, Type init, IDictionary<object, ICachedEntity> dictionary) where Entity : ICachedEntity
{
if (objectEntityCache.TryGetValue(init, out var cached))
if (dictionary.TryGetValue(init, out var cached))
return (Entity)cached;
using (StackGuard)
@@ -146,7 +161,7 @@ namespace Semmle.Extraction
var entity = factory.Create(this, init);
entity.Label = label;
objectEntityCache[init] = entity;
dictionary[init] = entity;
DefineLabel(entity, TrapWriter.Writer, Extractor);
if (entity.NeedsPopulation)
@@ -200,7 +215,17 @@ namespace Semmle.Extraction
#if DEBUG_LABELS
readonly Dictionary<string, ICachedEntity> idLabelCache = new Dictionary<string, ICachedEntity>();
#endif
readonly Dictionary<object, ICachedEntity> objectEntityCache = new Dictionary<object, ICachedEntity>();
class SymbolComparer : IEqualityComparer<object>
{
IEqualityComparer<ISymbol> comparer = SymbolEqualityComparer.IncludeNullability;
bool IEqualityComparer<object>.Equals(object x, object y) => comparer.Equals((ISymbol)x, (ISymbol)y);
int IEqualityComparer<object>.GetHashCode(object obj) => comparer.GetHashCode((ISymbol)obj);
}
readonly IDictionary<object, ICachedEntity> objectEntityCache = new Dictionary<object, ICachedEntity>();
readonly IDictionary<object, ICachedEntity> symbolEntityCache = new Dictionary<object, ICachedEntity>(10000, new SymbolComparer());
readonly Dictionary<ICachedEntity, Label> entityLabelCache = new Dictionary<ICachedEntity, Label>();
readonly HashSet<Label> extractedGenerics = new HashSet<Label>();

View File

@@ -130,6 +130,19 @@ namespace Semmle.Extraction
public static Entity CreateEntity<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity => cx.CreateEntity(factory, init);
/// <summary>
/// Creates and populates a new entity, or returns the existing one from the cache.
/// </summary>
/// <typeparam name="Type">The symbol type used to construct the entity.</typeparam>
/// <typeparam name="Entity">The type of the entity to create.</typeparam>
/// <param name="cx">The extractor context.</param>
/// <param name="factory">The factory used to construct the entity.</param>
/// <param name="init">The initializer for the entity, which may not be null.</param>
/// <returns>The entity.</returns>
public static Entity CreateEntityFromSymbol<Type, Entity>(this ICachedEntityFactory<Type, Entity> factory, Context cx, Type init)
where Entity : ICachedEntity
where Type : ISymbol => cx.CreateEntityFromSymbol(factory, init);
/// <summary>
/// Creates and populates a new entity, but uses a different cache.
/// </summary>