C#: Address more review comments

This commit is contained in:
Tom Hvitved
2020-09-02 10:43:50 +02:00
parent 51dc1515ab
commit 1b769ebac9
11 changed files with 67 additions and 139 deletions

View File

@@ -5,37 +5,16 @@ using System.Reflection;
namespace Semmle.Extraction.CSharp.Entities
{
/// <summary>
/// Provide a "Key" object to allow modifiers to exist as entities in the extractor
/// hash map. (Raw strings would work as keys but might clash with other types).
/// </summary>
class ModifierKey : Object
class Modifier : Extraction.CachedEntity<string>
{
public readonly string name;
public ModifierKey(string m)
{
name = m;
}
public override bool Equals(Object obj)
{
return obj.GetType() == GetType() && name == ((ModifierKey)obj).name;
}
public override int GetHashCode() => 13 * name.GetHashCode();
}
class Modifier : Extraction.CachedEntity<ModifierKey>
{
Modifier(Context cx, ModifierKey init)
Modifier(Context cx, string init)
: base(cx, init) { }
public override Microsoft.CodeAnalysis.Location ReportingLocation => null;
public override void WriteId(TextWriter trapFile)
{
trapFile.Write(symbol.name);
trapFile.Write(symbol);
trapFile.Write(";modifier");
}
@@ -43,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities
public override void Populate(TextWriter trapFile)
{
trapFile.modifiers(Label, symbol.name);
trapFile.modifiers(Label, symbol);
}
public static string AccessbilityModifier(Accessibility access)
@@ -154,21 +133,20 @@ namespace Semmle.Extraction.CSharp.Entities
public static Modifier Create(Context cx, string modifier)
{
var modifierKey = new ModifierKey(modifier);
return ModifierFactory.Instance.CreateEntity(cx, modifierKey, modifierKey);
return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier);
}
public static Modifier Create(Context cx, Accessibility access)
{
var modifierKey = new ModifierKey(AccessbilityModifier(access));
return ModifierFactory.Instance.CreateEntity(cx, modifierKey, modifierKey);
var modifier = AccessbilityModifier(access);
return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier);
}
class ModifierFactory : ICachedEntityFactory<ModifierKey, Modifier>
class ModifierFactory : ICachedEntityFactory<string, Modifier>
{
public static readonly ModifierFactory Instance = new ModifierFactory();
public Modifier Create(Context cx, ModifierKey init) => new Modifier(cx, init);
public Modifier Create(Context cx, string init) => new Modifier(cx, init);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
}

View File

@@ -202,8 +202,7 @@ namespace Semmle.Extraction.CSharp.Entities
return obj != null && obj.GetType() == typeof(VarargsType);
}
static readonly object cacheKey = new object();
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, cacheKey, null);
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, typeof(VarargsType), null);
class VarargsTypeFactory : ICachedEntityFactory<string, VarargsType>
{
@@ -238,8 +237,7 @@ namespace Semmle.Extraction.CSharp.Entities
return obj != null && obj.GetType() == typeof(VarargsParam);
}
static readonly object cacheKey = new object();
public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, cacheKey, method);
public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, typeof(VarargsParam), method);
class VarargsParamFactory : ICachedEntityFactory<Method, VarargsParam>
{
@@ -266,27 +264,8 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.param_location(this, Original.Location);
}
sealed class ConstructedExtensionParameterCacheKey
{
public readonly IParameterSymbol Parameter;
public readonly ITypeSymbol ConstructedType;
public ConstructedExtensionParameterCacheKey(IParameterSymbol parameter, ITypeSymbol constructedType)
{
Parameter = parameter;
ConstructedType = constructedType;
}
public override int GetHashCode() =>
(Parameter, ConstructedType).GetHashCode();
public override bool Equals(object obj) =>
obj is ConstructedExtensionParameterCacheKey k &&
SymbolEqualityComparer.Default.Equals(k.Parameter, Parameter) &&
SymbolEqualityComparer.Default.Equals(k.ConstructedType, ConstructedType);
}
public static ConstructedExtensionParameter Create(Context cx, Method method, Parameter parameter) =>
ExtensionParamFactory.Instance.CreateEntity(cx, new ConstructedExtensionParameterCacheKey(parameter.symbol, method.symbol.ReceiverType), (method, parameter));
ExtensionParamFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(parameter.symbol), new SymbolEqualityWrapper(method.symbol.ReceiverType)), (method, parameter));
class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter>
{

View File

@@ -1,3 +1,4 @@
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.CSharp.Entities.Expressions;
@@ -11,9 +12,13 @@ namespace Semmle.Extraction.CSharp.Entities
class Property : CachedSymbol<IPropertySymbol>, IExpressionParentEntity
{
protected Property(Context cx, IPropertySymbol init)
: base(cx, init) { }
: base(cx, init)
{
type = new Lazy<Type>(() => Type.Create(Context, symbol.Type));
}
Type Type => Type.Create(Context, symbol.Type);
readonly Lazy<Type> type;
Type Type => type.Value;
public override void WriteId(TextWriter trapFile)
{

View File

@@ -107,10 +107,25 @@ namespace Semmle.Extraction.CSharp.Entities
public override Microsoft.CodeAnalysis.Location ReportingLocation => GetLocations(symbol).FirstOrDefault();
bool IsAnonymousType() => symbol.IsAnonymousType || symbol.Name.Contains("__AnonymousType");
public override void WriteId(TextWriter trapFile)
{
symbol.BuildTypeId(Context, trapFile, symbol);
trapFile.Write(";type");
if (IsAnonymousType())
trapFile.Write('*');
else
{
symbol.BuildTypeId(Context, trapFile, symbol);
trapFile.Write(";type");
}
}
public override void WriteQuotedId(TextWriter trapFile)
{
if (IsAnonymousType())
trapFile.Write('*');
else
base.WriteQuotedId(trapFile);
}
/// <summary>
@@ -167,22 +182,10 @@ namespace Semmle.Extraction.CSharp.Entities
referencedType = Type.Create(cx, symbol);
}
sealed class NamedTypeRefCacheKey
{
public readonly INamedTypeSymbol Symbol;
public NamedTypeRefCacheKey(INamedTypeSymbol symbol) => Symbol = symbol;
public override int GetHashCode() =>
11 * Symbol.GetHashCode();
public override bool Equals(object obj) =>
obj is NamedTypeRefCacheKey k && SymbolEqualityComparer.IncludeNullability.Equals(k.Symbol, Symbol);
}
public static NamedTypeRef Create(Context cx, INamedTypeSymbol type) =>
// We need to use a different cache key than `type` to avoid mixing up
// `NamedType`s and `NamedTypeRef`s
NamedTypeRefFactory.Instance.CreateEntity(cx, new NamedTypeRefCacheKey(type), type);
NamedTypeRefFactory.Instance.CreateEntity(cx, (typeof(NamedTypeRef), new SymbolEqualityWrapper(type)), type);
class NamedTypeRefFactory : ICachedEntityFactory<INamedTypeSymbol, NamedTypeRef>
{

View File

@@ -27,8 +27,7 @@ namespace Semmle.Extraction.CSharp.Entities
return obj != null && obj.GetType() == typeof(NullType);
}
static readonly object cacheKey = new object();
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, cacheKey, null), NullableAnnotation.None);
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, typeof(NullType), null), NullableAnnotation.None);
class NullTypeFactory : ICachedEntityFactory<ITypeSymbol, NullType>
{

View File

@@ -292,22 +292,10 @@ namespace Semmle.Extraction.CSharp.Entities
DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original)
: base(cx, init, parent, original) { }
sealed class DelegateTypeParameterCacheKey
{
public readonly IParameterSymbol Symbol;
public DelegateTypeParameterCacheKey(IParameterSymbol symbol) => Symbol = symbol;
public override int GetHashCode() =>
13 * Symbol.GetHashCode();
public override bool Equals(object obj) =>
obj is DelegateTypeParameterCacheKey k && SymbolEqualityComparer.IncludeNullability.Equals(k.Symbol, Symbol);
}
new public static DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) =>
// We need to use a different cache key than `param` to avoid mixing up
// `DelegateTypeParameter`s and `Parameter`s
DelegateTypeParameterFactory.Instance.CreateEntity(cx, new DelegateTypeParameterCacheKey(param), (param, parent, original));
DelegateTypeParameterFactory.Instance.CreateEntity(cx, (typeof(DelegateTypeParameter), new SymbolEqualityWrapper(param)), (param, parent, original));
class DelegateTypeParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), DelegateTypeParameter>
{

View File

@@ -212,7 +212,7 @@ namespace Semmle.Extraction.CSharp
// #123 = @"C`1 : IEnumerable<__self___T>"
// ```
if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined))
trapFile.Write($"__self__");
trapFile.Write("__self__");
else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined))
type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass);
else
@@ -294,12 +294,7 @@ namespace Semmle.Extraction.CSharp
}
}
if (named.IsAnonymousType)
{
AddContaining();
named.BuildAnonymousName(cx, trapFile, (s0, cx0, trapFile0) => BuildOrWriteId(s0, cx0, trapFile0, symbolBeingDefined, addBaseClass), true);
}
else if (named.TypeParameters.IsEmpty)
if (named.TypeParameters.IsEmpty)
{
AddContaining();
trapFile.Write(named.Name);
@@ -310,20 +305,6 @@ namespace Semmle.Extraction.CSharp
trapFile.Write(named.Name);
trapFile.Write("`");
trapFile.Write(named.TypeParameters.Length);
// Some types such as `<>f__AnonymousType0` are not considered anonymous types by Roslyn,
// perhaps because they contain type parameters. We still need to treat them like anonymous
// types, though, by adding the underlying properties, in order to disambiguate them
if (named.Name.Contains("__AnonymousType"))
{
trapFile.Write('<');
trapFile.BuildList(",", named.GetMembers().OfType<IPropertySymbol>(), (prop, tb0) =>
{
tb0.Write(prop.Name);
tb0.Write(" ");
prop.Type.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass);
});
trapFile.Write('>');
}
}
else
{
@@ -364,22 +345,14 @@ namespace Semmle.Extraction.CSharp
trapFile.Write('.');
}
static void BuildAnonymousName(this INamedTypeSymbol type, Context cx, TextWriter trapFile, Action<ITypeSymbol, Context, TextWriter> subTermAction, bool includeParamName)
static void BuildAnonymousName(this INamedTypeSymbol type, Context cx, TextWriter trapFile)
{
var buildParam = includeParamName
? (prop, tb0) =>
{
tb0.Write(prop.Name);
tb0.Write(' ');
subTermAction(prop.Type, cx, tb0);
}
: (Action<IPropertySymbol, TextWriter>)((prop, tb0) => subTermAction(prop.Type, cx, tb0));
int memberCount = type.GetMembers().OfType<IPropertySymbol>().Count();
int hackTypeNumber = memberCount == 1 ? 1 : 0;
trapFile.Write("<>__AnonType");
trapFile.Write(hackTypeNumber);
trapFile.Write('<');
trapFile.BuildList(",", type.GetMembers().OfType<IPropertySymbol>(), buildParam);
trapFile.BuildList(",", type.GetMembers().OfType<IPropertySymbol>(), (prop, tb0) => BuildDisplayName(prop.Type, cx, tb0));
trapFile.Write('>');
}
@@ -444,11 +417,9 @@ namespace Semmle.Extraction.CSharp
}
if (namedType.IsAnonymousType)
{
namedType.BuildAnonymousName(cx, trapFile, (sub, cx0, tb0) => sub.BuildDisplayName(cx0, tb0), false);
}
trapFile.Write(namedType.Name);
namedType.BuildAnonymousName(cx, trapFile);
else
trapFile.Write(namedType.Name);
if (namedType.IsGenericType && namedType.TypeKind != TypeKind.Error && namedType.TypeArguments.Any())
{
trapFile.Write('<');

View File

@@ -89,8 +89,7 @@ namespace Semmle.Extraction.Entities
public static string PathAsDatabaseString(string path) => path.Replace('\\', '/');
static readonly object cacheKey = new object();
public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, (cacheKey, path), path);
public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, (typeof(File), path), path);
public static File CreateGenerated(Context cx) => GeneratedFile.Create(cx);
@@ -112,7 +111,7 @@ namespace Semmle.Extraction.Entities
}
public static GeneratedFile Create(Context cx) =>
GeneratedFileFactory.Instance.CreateEntity(cx, cacheKey, null);
GeneratedFileFactory.Instance.CreateEntity(cx, typeof(GeneratedFile), null);
class GeneratedFileFactory : ICachedEntityFactory<string?, GeneratedFile>
{

View File

@@ -28,8 +28,7 @@ namespace Semmle.Extraction.Entities
public override bool Equals(object? obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
static readonly object cacheKey = new object();
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, cacheKey, null);
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
{

View File

@@ -1,4 +1,5 @@
using System.IO;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction
{
@@ -75,4 +76,20 @@ namespace Semmle.Extraction
public abstract TrapStackBehaviour TrapStackBehaviour { get; }
}
/// <summary>
/// A class used to wrap an `ISymbol` object, which uses `SymbolEqualityComparer.Default`
/// for comparison.
/// </summary>
public sealed class 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 * Symbol.GetHashCode();
}
}

View File

@@ -56,17 +56,7 @@ private class ThrowingCall extends NonReturningCall {
}
/** Holds if accessor `a` has an auto-implementation. */
private predicate hasAccessorAutoImplementation(Accessor a) {
exists(SourceLocation sl | sl = a.getALocation() |
not exists(ControlFlowElement cfe, Location l | sl.getFile() = l.getFile() |
stmt_parent_top_level(cfe, _, a) and
stmt_location(cfe, l)
or
expr_parent_top_level_adjusted(cfe, 0, a) and
expr_location(cfe, l)
)
)
}
private predicate hasAccessorAutoImplementation(Accessor a) { not a.hasBody() }
abstract private class NonReturningCallable extends Callable {
NonReturningCallable() {