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 { 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(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(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(). 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().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"; } } }