Files
codeql/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs
2018-10-10 14:40:52 +01:00

182 lines
7.2 KiB
C#

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
class Invocation : Expression<InvocationExpressionSyntax>
{
Invocation(ExpressionNodeInfo info)
: base(info.SetKind(GetKind(info)))
{
this.info = info;
}
readonly ExpressionNodeInfo info;
public static Expression Create(ExpressionNodeInfo info) => new Invocation(info).TryPopulate();
protected override void Populate()
{
if (IsNameof(Syntax))
{
PopulateArguments(cx, Syntax.ArgumentList, 0);
return;
}
var child = -1;
string memberName = null;
var target = TargetSymbol;
switch (Syntax.Expression)
{
case MemberAccessExpressionSyntax memberAccess:
memberName = memberAccess.Name.Identifier.Text;
if (Syntax.Expression.Kind() == SyntaxKind.SimpleMemberAccessExpression)
// Qualified method call; `x.M()`
Create(cx, memberAccess.Expression, this, child++);
else
// Pointer member access; `x->M()`
Create(cx, Syntax.Expression, this, child++);
break;
case MemberBindingExpressionSyntax memberBinding:
// Conditionally qualified method call; `x?.M()`
memberName = memberBinding.Name.Identifier.Text;
Create(cx, FindConditionalQualifier(memberBinding), this, child++);
MakeConditional();
break;
case SimpleNameSyntax simpleName when (Kind == ExprKind.METHOD_INVOCATION):
// Unqualified method call; `M()`
memberName = simpleName.Identifier.Text;
if (target != null && !target.IsStatic)
{
// Implicit `this` qualifier; add explicitly
var callingMethod = cx.Model(Syntax).GetEnclosingSymbol(Location.symbol.SourceSpan.Start) as IMethodSymbol;
if (callingMethod == null)
cx.ModelError(Syntax, "Couldn't determine implicit this type");
else
This.CreateImplicit(cx, Type.Create(cx, callingMethod.ContainingType), Location, this, child++);
}
else
{
// No implicit `this` qualifier
child++;
}
break;
default:
// Delegate call; `d()`
Create(cx, Syntax.Expression, this, child++);
break;
}
var isDynamicCall = IsDynamicCall(info);
if (isDynamicCall)
{
if (memberName != null)
cx.Emit(Tuples.dynamic_member_name(this, memberName));
else
cx.ModelError(Syntax, "Unable to get name for dynamic call.");
}
PopulateArguments(cx, Syntax.ArgumentList, child);
if (target == null)
{
if (!isDynamicCall && !IsDelegateCall(info))
cx.ModelError(Syntax, "Unable to resolve target for call. (Compilation error?)");
return;
}
var targetKey = Method.Create(cx, target);
cx.Emit(Tuples.expr_call(this, targetKey));
}
static bool IsDynamicCall(ExpressionNodeInfo info)
{
// Either the qualifier (Expression) is dynamic,
// or one of the arguments is dynamic.
var node = (InvocationExpressionSyntax)info.Node;
return !IsDelegateCall(info) &&
(IsDynamic(info.Context, node.Expression) || node.ArgumentList.Arguments.Any(arg => IsDynamic(info.Context, arg.Expression)));
}
public SymbolInfo SymbolInfo => info.SymbolInfo;
public IMethodSymbol TargetSymbol
{
get
{
var si = SymbolInfo;
if (si.Symbol != null) return si.Symbol as IMethodSymbol;
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<IMethodSymbol>().
Where(method => method.Parameters.Length >= Syntax.ArgumentList.Arguments.Count).
Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= Syntax.ArgumentList.Arguments.Count);
return cx.Extractor.Standalone ?
candidates.FirstOrDefault() :
candidates.SingleOrDefault();
}
return si.Symbol as IMethodSymbol;
}
}
static bool IsDelegateCall(ExpressionNodeInfo info)
{
var si = info.SymbolInfo;
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure &&
si.CandidateSymbols.OfType<IMethodSymbol>().All(s => s.MethodKind == MethodKind.DelegateInvoke))
return true;
// Delegate variable is a dynamic
var node = (InvocationExpressionSyntax)info.Node;
if (si.CandidateReason == CandidateReason.LateBound &&
node.Expression is IdentifierNameSyntax &&
IsDynamic(info.Context, node.Expression) &&
si.Symbol == null)
return true;
return si.Symbol != null &&
si.Symbol.Kind == SymbolKind.Method &&
((IMethodSymbol)si.Symbol).MethodKind == MethodKind.DelegateInvoke;
}
static bool IsLocalFunctionInvocation(ExpressionNodeInfo info)
{
var target = info.SymbolInfo.Symbol as IMethodSymbol;
return target != null && target.MethodKind == MethodKind.LocalFunction;
}
static ExprKind GetKind(ExpressionNodeInfo info)
{
return IsNameof((InvocationExpressionSyntax)info.Node) ?
ExprKind.NAMEOF :
IsDelegateCall(info) ?
ExprKind.DELEGATE_INVOCATION :
IsLocalFunctionInvocation(info) ?
ExprKind.LOCAL_FUNCTION_INVOCATION :
ExprKind.METHOD_INVOCATION;
}
static bool IsNameof(InvocationExpressionSyntax syntax)
{
// Odd that this is not a separate expression type.
// Maybe it will be in the future.
var id = syntax.Expression as IdentifierNameSyntax;
return id != null && id.Identifier.Text == "nameof";
}
}
}