Skip to content

Commit a8f2d3b

Browse files
committed
C#: Re-use the GetTargetSymbol logic from invocations to find the right operator symbol (operators can also be declared in extensions).
1 parent ee2ab37 commit a8f2d3b

7 files changed

Lines changed: 44 additions & 43 deletions

File tree

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

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,41 @@ type.SpecialType is SpecialType.System_IntPtr ||
228228
return Literal.CreateGenerated(cx, parent, childIndex, type, defaultValue, location);
229229
}
230230

231+
/// <summary>
232+
/// Given an expression syntax node, attempt to resolve the target method symbol for it.
233+
/// The operation takes extension methods into account.
234+
/// </summary>
235+
/// <param name="node">The expression syntax node.</param>
236+
/// <returns>Returns the target method symbol, or null if it cannot be resolved.</returns>
237+
protected IMethodSymbol? GetTargetSymbol(ExpressionSyntax node)
238+
{
239+
var si = Context.GetSymbolInfo(node);
240+
if (si.Symbol is ISymbol symbol)
241+
{
242+
var method = symbol as IMethodSymbol;
243+
// Case for compiler-generated extension methods.
244+
return method?.TryGetExtensionMethod() ?? method;
245+
}
246+
247+
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure && node is InvocationExpressionSyntax syntax)
248+
{
249+
// This seems to be a bug in Roslyn
250+
// For some reason, typeof(X).InvokeMember(...) fails to resolve the correct
251+
// InvokeMember() method, even though the number of parameters clearly identifies the correct method
252+
253+
var candidates = si.CandidateSymbols
254+
.OfType<IMethodSymbol>()
255+
.Where(method => method.Parameters.Length >= syntax.ArgumentList.Arguments.Count)
256+
.Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= syntax.ArgumentList.Arguments.Count);
257+
258+
return Context.ExtractionContext.IsStandalone ?
259+
candidates.FirstOrDefault() :
260+
candidates.SingleOrDefault();
261+
}
262+
263+
return si.Symbol as IMethodSymbol;
264+
}
265+
231266
/// <summary>
232267
/// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call.
233268
/// </summary>
@@ -244,10 +279,10 @@ public static ExprKind UnaryOperatorKind(Context cx, ExprKind originalKind, Expr
244279
/// name if available.
245280
/// </summary>
246281
/// <param name="node">The expression.</param>
247-
public void OperatorCall(TextWriter trapFile, ExpressionSyntax node)
282+
public void AddOperatorCall(TextWriter trapFile, ExpressionSyntax node)
248283
{
249-
var @operator = Context.GetSymbolInfo(node);
250-
if (@operator.Symbol is IMethodSymbol method)
284+
var @operator = GetTargetSymbol(node);
285+
if (@operator is IMethodSymbol method)
251286
{
252287
var callType = GetCallType(Context, node);
253288
if (callType == CallType.Dynamic)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ protected override void PopulateExpression(TextWriter trapFile)
2424
{
2525
Create(Context, Syntax.Left, this, 0);
2626
Create(Context, Syntax.Right, this, 1);
27-
2827
if (Kind != ExprKind.SIMPLE_ASSIGN && Kind != ExprKind.ASSIGN_COALESCE)
2928
{
30-
OperatorCall(trapFile, Syntax);
29+
AddOperatorCall(trapFile, Syntax);
3130
}
3231
}
3332

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ private void CreateDeferred(Context cx, ExpressionSyntax node, int child)
4040

4141
protected override void PopulateExpression(TextWriter trapFile)
4242
{
43-
OperatorCall(trapFile, Syntax);
43+
AddOperatorCall(trapFile, Syntax);
4444
CreateDeferred(Context, Syntax.Left, 0);
4545
CreateDeferred(Context, Syntax.Right, 1);
4646
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ protected override void PopulateExpression(TextWriter trapFile)
2525
else
2626
{
2727
// Type conversion
28-
OperatorCall(trapFile, Syntax);
28+
AddOperatorCall(trapFile, Syntax);
2929
TypeMention.Create(Context, Syntax.Type, this, Type);
3030
}
3131
}

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

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ protected override void PopulateExpression(TextWriter trapFile)
4444

4545
var child = -1;
4646
string? memberName = null;
47-
var target = TargetSymbol;
47+
var target = GetTargetSymbol(Syntax);
4848
switch (Syntax.Expression)
4949
{
5050
case MemberAccessExpressionSyntax memberAccess when IsValidMemberAccessKind():
@@ -129,39 +129,6 @@ private static bool IsOperatorLikeCall(ExpressionNodeInfo info)
129129
method.TryGetExtensionMethod()?.MethodKind == MethodKind.UserDefinedOperator;
130130
}
131131

132-
public IMethodSymbol? TargetSymbol
133-
{
134-
get
135-
{
136-
var si = SymbolInfo;
137-
138-
if (si.Symbol is ISymbol symbol)
139-
{
140-
var method = symbol as IMethodSymbol;
141-
// Case for compiler-generated extension methods.
142-
return method?.TryGetExtensionMethod() ?? method;
143-
}
144-
145-
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure)
146-
{
147-
// This seems to be a bug in Roslyn
148-
// For some reason, typeof(X).InvokeMember(...) fails to resolve the correct
149-
// InvokeMember() method, even though the number of parameters clearly identifies the correct method
150-
151-
var candidates = si.CandidateSymbols
152-
.OfType<IMethodSymbol>()
153-
.Where(method => method.Parameters.Length >= Syntax.ArgumentList.Arguments.Count)
154-
.Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= Syntax.ArgumentList.Arguments.Count);
155-
156-
return Context.ExtractionContext.IsStandalone ?
157-
candidates.FirstOrDefault() :
158-
candidates.SingleOrDefault();
159-
}
160-
161-
return si.Symbol as IMethodSymbol;
162-
}
163-
}
164-
165132
private static bool IsDelegateLikeCall(ExpressionNodeInfo info)
166133
{
167134
return IsDelegateLikeCall(info, symbol => IsFunctionPointer(symbol) || IsDelegateInvoke(symbol));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ protected override void PopulateExpression(TextWriter trapFile)
2525
if ((operatorKind == ExprKind.POST_INCR || operatorKind == ExprKind.POST_DECR) &&
2626
Kind == ExprKind.OPERATOR_INVOCATION)
2727
{
28-
OperatorCall(trapFile, Syntax);
28+
AddOperatorCall(trapFile, Syntax);
2929
trapFile.mutator_invocation_mode(this, 2);
3030
}
3131
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static Unary Create(ExpressionNodeInfo info)
2424
protected override void PopulateExpression(TextWriter trapFile)
2525
{
2626
Create(Context, Syntax.Operand, this, 0);
27-
OperatorCall(trapFile, Syntax);
27+
AddOperatorCall(trapFile, Syntax);
2828

2929
if ((operatorKind == ExprKind.PRE_INCR || operatorKind == ExprKind.PRE_DECR) &&
3030
Kind == ExprKind.OPERATOR_INVOCATION)

0 commit comments

Comments
 (0)