diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
index 93107fc6dab..4ab90def2c1 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
@@ -228,6 +228,41 @@ namespace Semmle.Extraction.CSharp.Entities
return Literal.CreateGenerated(cx, parent, childIndex, type, defaultValue, location);
}
+ ///
+ /// Given an expression syntax node, attempt to resolve the target method symbol for it.
+ /// The operation takes extension methods into account.
+ ///
+ /// The expression syntax node.
+ /// Returns the target method symbol, or null if it cannot be resolved.
+ protected IMethodSymbol? GetTargetSymbol(ExpressionSyntax node)
+ {
+ var si = Context.GetSymbolInfo(node);
+ if (si.Symbol is ISymbol symbol)
+ {
+ var method = symbol as IMethodSymbol;
+ // Case for compiler-generated extension methods.
+ return method?.TryGetExtensionMethod() ?? method;
+ }
+
+ if (si.CandidateReason == CandidateReason.OverloadResolutionFailure && node is InvocationExpressionSyntax syntax)
+ {
+ // This seems to be a bug in Roslyn
+ // For some reason, typeof(X).InvokeMember(...) fails to resolve the correct
+ // InvokeMember() method, even though the number of parameters clearly identifies the correct method
+
+ var candidates = si.CandidateSymbols
+ .OfType()
+ .Where(method => method.Parameters.Length >= syntax.ArgumentList.Arguments.Count)
+ .Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= syntax.ArgumentList.Arguments.Count);
+
+ return Context.ExtractionContext.IsStandalone ?
+ candidates.FirstOrDefault() :
+ candidates.SingleOrDefault();
+ }
+
+ return si.Symbol as IMethodSymbol;
+ }
+
///
/// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call.
///
@@ -244,10 +279,10 @@ namespace Semmle.Extraction.CSharp.Entities
/// name if available.
///
/// The expression.
- public void OperatorCall(TextWriter trapFile, ExpressionSyntax node)
+ public void AddOperatorCall(TextWriter trapFile, ExpressionSyntax node)
{
- var @operator = Context.GetSymbolInfo(node);
- if (@operator.Symbol is IMethodSymbol method)
+ var @operator = GetTargetSymbol(node);
+ if (@operator is IMethodSymbol method)
{
var callType = GetCallType(Context, node);
if (callType == CallType.Dynamic)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
index 67e49b2919c..6d869b256c5 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
@@ -24,10 +24,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
Create(Context, Syntax.Left, this, 0);
Create(Context, Syntax.Right, this, 1);
-
if (Kind != ExprKind.SIMPLE_ASSIGN && Kind != ExprKind.ASSIGN_COALESCE)
{
- OperatorCall(trapFile, Syntax);
+ AddOperatorCall(trapFile, Syntax);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
index d4cc5cc81d5..2bc8c61f21f 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
@@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected override void PopulateExpression(TextWriter trapFile)
{
- OperatorCall(trapFile, Syntax);
+ AddOperatorCall(trapFile, Syntax);
CreateDeferred(Context, Syntax.Left, 0);
CreateDeferred(Context, Syntax.Right, 1);
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs
index 20a5dc611a3..c11711f3092 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs
@@ -25,7 +25,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
else
{
// Type conversion
- OperatorCall(trapFile, Syntax);
+ AddOperatorCall(trapFile, Syntax);
TypeMention.Create(Context, Syntax.Type, this, Type);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs
index 2ed7aec9955..343f288eeaf 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs
@@ -44,7 +44,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
var child = -1;
string? memberName = null;
- var target = TargetSymbol;
+ var target = GetTargetSymbol(Syntax);
switch (Syntax.Expression)
{
case MemberAccessExpressionSyntax memberAccess when IsValidMemberAccessKind():
@@ -129,39 +129,6 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
method.TryGetExtensionMethod()?.MethodKind == MethodKind.UserDefinedOperator;
}
- public IMethodSymbol? TargetSymbol
- {
- get
- {
- var si = SymbolInfo;
-
- if (si.Symbol is ISymbol symbol)
- {
- var method = symbol as IMethodSymbol;
- // Case for compiler-generated extension methods.
- return method?.TryGetExtensionMethod() ?? method;
- }
-
- if (si.CandidateReason == CandidateReason.OverloadResolutionFailure)
- {
- // This seems to be a bug in Roslyn
- // For some reason, typeof(X).InvokeMember(...) fails to resolve the correct
- // InvokeMember() method, even though the number of parameters clearly identifies the correct method
-
- var candidates = si.CandidateSymbols
- .OfType()
- .Where(method => method.Parameters.Length >= Syntax.ArgumentList.Arguments.Count)
- .Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= Syntax.ArgumentList.Arguments.Count);
-
- return Context.ExtractionContext.IsStandalone ?
- candidates.FirstOrDefault() :
- candidates.SingleOrDefault();
- }
-
- return si.Symbol as IMethodSymbol;
- }
- }
-
private static bool IsDelegateLikeCall(ExpressionNodeInfo info)
{
return IsDelegateLikeCall(info, symbol => IsFunctionPointer(symbol) || IsDelegateInvoke(symbol));
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs
index dbe5ecb3d18..051a03e9f8c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs
@@ -25,7 +25,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if ((operatorKind == ExprKind.POST_INCR || operatorKind == ExprKind.POST_DECR) &&
Kind == ExprKind.OPERATOR_INVOCATION)
{
- OperatorCall(trapFile, Syntax);
+ AddOperatorCall(trapFile, Syntax);
trapFile.mutator_invocation_mode(this, 2);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs
index 161fbe62e3f..699c3810d11 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs
@@ -24,7 +24,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected override void PopulateExpression(TextWriter trapFile)
{
Create(Context, Syntax.Operand, this, 0);
- OperatorCall(trapFile, Syntax);
+ AddOperatorCall(trapFile, Syntax);
if ((operatorKind == ExprKind.PRE_INCR || operatorKind == ExprKind.PRE_DECR) &&
Kind == ExprKind.OPERATOR_INVOCATION)