Skip to content

Commit 359934c

Browse files
committed
C#: Static extension methods in extension types should also have their call target replaced.
1 parent c5af880 commit 359934c

File tree

2 files changed

+26
-13
lines changed

2 files changed

+26
-13
lines changed

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

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

910
namespace Semmle.Extraction.CSharp
1011
{
@@ -631,8 +632,30 @@ public static bool IsSourceDeclaration(this IParameterSymbol parameter)
631632
/// Return true if this method is a compiler-generated extension method.
632633
/// </summary>
633634
public static bool IsCompilerGeneratedExtensionMethod(this IMethodSymbol method) =>
634-
method.IsImplicitlyDeclared && method.IsExtensionMethod;
635+
method.TryGetExtensionMethod(out _);
635636

637+
/// <summary>
638+
/// Returns true if this method is a compiler-generated extension method,
639+
/// and outputs the original extension method declaration.
640+
/// </summary>
641+
public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodSymbol? declaration)
642+
{
643+
declaration = null;
644+
if (method.IsImplicitlyDeclared && method.ContainingSymbol is INamedTypeSymbol containingType)
645+
{
646+
// Extension types are declared within the same type as the generated
647+
// extension method implementation.
648+
var extensions = containingType.GetMembers()
649+
.OfType<INamedTypeSymbol>()
650+
.Where(t => t.IsExtension);
651+
// Find the original extension method that maps to this implementation (if any).
652+
declaration = extensions.SelectMany(e => e.GetMembers())
653+
.OfType<IMethodSymbol>()
654+
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method));
655+
return declaration is not null;
656+
}
657+
return false;
658+
}
636659
/// <summary>
637660
/// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base
638661
/// types of type parameters as well as `object` base types.

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,9 @@ public IMethodSymbol? TargetSymbol
124124
var method = symbol as IMethodSymbol;
125125
// Case for compiler-generated extension methods.
126126
if (method is not null &&
127-
method.IsCompilerGeneratedExtensionMethod() &&
128-
method.ContainingSymbol is INamedTypeSymbol containingType)
127+
method.TryGetExtensionMethod(out var original))
129128
{
130-
// Extension types are declared within the same type as the generated
131-
// extension method implementation.
132-
var extensions = containingType.GetMembers()
133-
.OfType<INamedTypeSymbol>()
134-
.Where(t => t.IsExtension);
135-
// Find the original extension method that maps to this implementation (if any).
136-
var original = extensions.SelectMany(e => e.GetMembers())
137-
.OfType<IMethodSymbol>()
138-
.FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method));
139-
return original ?? method;
129+
return original;
140130
}
141131

142132
return method;

0 commit comments

Comments
 (0)