Merge pull request #5980 from tamasvajk/fix/extension-method-as-target

C#: Extract correct method symbol as target of extension method calls
This commit is contained in:
Tamás Vajk
2021-06-07 15:57:24 +02:00
committed by GitHub
7 changed files with 124 additions and 85 deletions

View File

@@ -20,26 +20,6 @@ namespace Semmle.Extraction.CSharp.Entities
IEnumerable<IParameterSymbol> parameters = Symbol.Parameters;
IEnumerable<IParameterSymbol> originalParameters = originalMethod.Symbol.Parameters;
if (IsReducedExtension)
{
if (this == originalMethod)
{
// Non-generic reduced extensions must be extracted exactly like the
// non-reduced counterparts
parameters = Symbol.ReducedFrom!.Parameters;
}
else
{
// Constructed reduced extensions are special because their non-reduced
// counterparts are not constructed. Therefore, we need to manually add
// the `this` parameter based on the type of the receiver
var originalThisParamSymbol = originalMethod.Symbol.Parameters.First();
var originalThisParam = Parameter.Create(Context, originalThisParamSymbol, originalMethod);
ConstructedExtensionParameter.Create(Context, this, originalThisParam);
originalParameters = originalParameters.Skip(1);
}
}
foreach (var p in parameters.Zip(originalParameters, (paramSymbol, originalParam) => new { paramSymbol, originalParam }))
{
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam)
@@ -208,9 +188,7 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.Write('(');
var index = 0;
var @params = method.MethodKind == MethodKind.ReducedExtension
? method.ReducedFrom!.Parameters
: method.Parameters;
var @params = method.Parameters;
foreach (var param in @params)
{
@@ -272,6 +250,11 @@ namespace Semmle.Extraction.CSharp.Entities
case MethodKind.Constructor:
return Constructor.Create(cx, methodDecl);
case MethodKind.ReducedExtension:
if (SymbolEqualityComparer.Default.Equals(methodDecl, methodDecl.ConstructedFrom))
{
return OrdinaryMethod.Create(cx, methodDecl.ReducedFrom!);
}
return OrdinaryMethod.Create(cx, methodDecl.ReducedFrom!.Construct(methodDecl.TypeArguments, methodDecl.TypeArgumentNullableAnnotations));
case MethodKind.Ordinary:
case MethodKind.DelegateInvoke:
return OrdinaryMethod.Create(cx, methodDecl);
@@ -297,10 +280,7 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
public Method OriginalDefinition =>
IsReducedExtension
? Create(Context, Symbol.ReducedFrom!)
: Create(Context, Symbol.OriginalDefinition);
public Method OriginalDefinition => Create(Context, Symbol.OriginalDefinition);
public override Location? FullLocation => ReportingLocation;
@@ -318,9 +298,7 @@ namespace Semmle.Extraction.CSharp.Entities
public bool IsBoundGeneric => IsGeneric && !IsUnboundGeneric;
private bool IsReducedExtension => Symbol.MethodKind == MethodKind.ReducedExtension;
protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom.ReducedFrom ?? Symbol.ConstructedFrom;
protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom;
bool IExpressionParentEntity.IsTopLevelParent => true;

View File

@@ -15,14 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities
protected override IMethodSymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol;
public IMethodSymbol SourceDeclaration
{
get
{
var reducedFrom = Symbol.ReducedFrom ?? Symbol;
return reducedFrom.OriginalDefinition;
}
}
public IMethodSymbol SourceDeclaration => Symbol.OriginalDefinition;
public override Microsoft.CodeAnalysis.Location ReportingLocation => Symbol.GetSymbolLocation();
@@ -53,7 +46,15 @@ namespace Semmle.Extraction.CSharp.Entities
ExtractCompilerGenerated(trapFile);
}
public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method);
public static new OrdinaryMethod Create(Context cx, IMethodSymbol method)
{
if (method.MethodKind == MethodKind.ReducedExtension)
{
cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, "Reduced extension method symbols should not be directly extracted.");
}
return OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method);
}
private class OrdinaryMethodFactory : CachedEntityFactory<IMethodSymbol, OrdinaryMethod>
{

View File

@@ -27,20 +27,7 @@ namespace Semmle.Extraction.CSharp.Entities
None, Ref, Out, Params, This, In
}
protected virtual int Ordinal
{
get
{
// For some reason, methods of kind ReducedExtension
// omit the "this" parameter, so the parameters are
// actually numbered from 1.
// This is to be consistent from the original (unreduced) extension method.
var isReducedExtension =
Symbol.ContainingSymbol is IMethodSymbol method &&
method.MethodKind == MethodKind.ReducedExtension;
return Symbol.Ordinal + (isReducedExtension ? 1 : 0);
}
}
protected virtual int Ordinal => Symbol.Ordinal;
private Kind ParamKind
{
@@ -270,33 +257,4 @@ namespace Semmle.Extraction.CSharp.Entities
public override VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init);
}
}
internal class ConstructedExtensionParameter : Parameter
{
private readonly ITypeSymbol constructedType;
private ConstructedExtensionParameter(Context cx, Method method, Parameter original)
: base(cx, original.Symbol, method, original)
{
constructedType = method.Symbol.ReceiverType!;
}
public override void Populate(TextWriter trapFile)
{
var typeKey = Type.Create(Context, constructedType);
trapFile.@params(this, Original.Symbol.Name, typeKey.TypeRef, 0, Kind.This, Parent!, Original);
trapFile.param_location(this, Original.Location);
}
public static ConstructedExtensionParameter Create(Context cx, Method method, Parameter parameter) =>
ExtensionParamFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(parameter.Symbol), new SymbolEqualityWrapper(method.Symbol.ReceiverType!)), (method, parameter));
private class ExtensionParamFactory : CachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter>
{
public static ExtensionParamFactory Instance { get; } = new ExtensionParamFactory();
public override ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) =>
new ConstructedExtensionParameter(cx, init.Item1, init.Item2);
}
}
}