mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user