Skip to content

Commit 240c637

Browse files
committed
C#: Replace calls to constructed generic extensions methods with calls to constructed extensions from extension types (if possible).
1 parent ec5b3ee commit 240c637

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Linq;
66
using Microsoft.CodeAnalysis;
77
using Semmle.Extraction.CSharp.Entities;
8-
using Semmle.Extraction.CSharp.Entities.Statements;
98

109
namespace Semmle.Extraction.CSharp
1110
{
@@ -647,14 +646,36 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS
647646
var extensions = containingType.GetMembers()
648647
.OfType<INamedTypeSymbol>()
649648
.Where(t => t.IsExtension);
650-
// Find the original extension method that maps to this implementation (if any).
651-
declaration = extensions.SelectMany(e => e.GetMembers())
649+
// Find the (possibly unbound) original extension method that maps to this implementation (if any).
650+
var unboundDeclaration = extensions.SelectMany(e => e.GetMembers())
652651
.OfType<IMethodSymbol>()
653-
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method));
654-
return declaration is not null;
652+
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom));
653+
654+
var isFullyConstructed = method.IsBoundGenericMethod();
655+
if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType && extensionType.IsGenericType)
656+
{
657+
// Use the type arguments from the constructed extension method to construct the extension type.
658+
var arguments = method.TypeArguments.ToArray();
659+
var boundExtensionType = extensionType.Construct(arguments);
660+
var boundDeclaration = boundExtensionType.GetMembers()
661+
.OfType<IMethodSymbol>()
662+
.FirstOrDefault(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration));
663+
declaration = boundDeclaration;
664+
}
665+
else
666+
{
667+
declaration = unboundDeclaration;
668+
}
669+
655670
}
656-
return false;
671+
return declaration is not null;
657672
}
673+
674+
public static bool IsUnboundGenericMethod(this IMethodSymbol method) =>
675+
method.IsGenericMethod && SymbolEqualityComparer.Default.Equals(method.ConstructedFrom, method);
676+
677+
public static bool IsBoundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && !IsUnboundGenericMethod(method);
678+
658679
/// <summary>
659680
/// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base
660681
/// types of type parameters as well as `object` base types.

csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,9 @@ public static void AddExplicitInterfaceQualifierToId(Context cx, EscapingTextWri
321321
/// <summary>
322322
/// Whether this method has unbound type parameters.
323323
/// </summary>
324-
public bool IsUnboundGeneric => IsGeneric && SymbolEqualityComparer.Default.Equals(Symbol.ConstructedFrom, Symbol);
324+
public bool IsUnboundGeneric => Symbol.IsUnboundGenericMethod();
325325

326-
public bool IsBoundGeneric => IsGeneric && !IsUnboundGeneric;
326+
public bool IsBoundGeneric => Symbol.IsBoundGenericMethod();
327327

328328
protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom;
329329

0 commit comments

Comments
 (0)