// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
///
/// This portion of the binder converts an into a .
///
internal partial class Binder
{
private BoundExpression BindMethodGroup(ExpressionSyntax node, bool invoked, bool indexed, DiagnosticBag diagnostics)
{
switch (node.Kind())
{
case SyntaxKind.IdentifierName:
case SyntaxKind.GenericName:
return BindIdentifier((SimpleNameSyntax)node, invoked, diagnostics);
case SyntaxKind.SimpleMemberAccessExpression:
case SyntaxKind.PointerMemberAccessExpression:
return BindMemberAccess((MemberAccessExpressionSyntax)node, invoked, indexed, diagnostics);
case SyntaxKind.ParenthesizedExpression:
return BindMethodGroup(((ParenthesizedExpressionSyntax)node).Expression, invoked: false, indexed: false, diagnostics: diagnostics);
default:
return BindExpression(node, diagnostics, invoked, indexed);
}
}
private static ImmutableArray GetOriginalMethods(OverloadResolutionResult overloadResolutionResult)
{
// If overload resolution has failed then we want to stash away the original methods that we
// considered so that the IDE can display tooltips or other information about them.
// However, if a method group contained a generic method that was type inferred then
// the IDE wants information about the *inferred* method, not the original unconstructed
// generic method.
if (overloadResolutionResult == null)
{
return ImmutableArray.Empty;
}
var builder = ArrayBuilder.GetInstance();
foreach (var result in overloadResolutionResult.Results)
{
builder.Add(result.Member);
}
return builder.ToImmutableAndFree();
}
///
/// Helper method to create a synthesized method invocation expression.
///
/// Syntax Node.
/// Receiver for the method call.
/// Method to be invoked on the receiver.
/// Arguments to the method call.
/// Diagnostics.
/// Optional type arguments syntax.
/// Optional type arguments.
/// The syntax for the query clause generating this invocation expression, if any.
/// True to allow invocation of fields and properties of delegate type. Only methods are allowed otherwise.
/// False to prevent selecting a params method in unexpanded form.
/// Synthesized method invocation expression.
internal BoundExpression MakeInvocationExpression(
SyntaxNode node,
BoundExpression receiver,
string methodName,
ImmutableArray args,
DiagnosticBag diagnostics,
SeparatedSyntaxList typeArgsSyntax = default(SeparatedSyntaxList),
ImmutableArray typeArgs = default(ImmutableArray),
CSharpSyntaxNode queryClause = null,
bool allowFieldsAndProperties = false,
bool allowUnexpandedForm = true)
{
Debug.Assert(receiver != null);
var boundExpression = BindInstanceMemberAccess(node, node, receiver, methodName, typeArgs.NullToEmpty().Length, typeArgsSyntax, typeArgs, true, diagnostics);
// The other consumers of this helper (await and collection initializers) require the target member to be a method.
if (!allowFieldsAndProperties && (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess))
{
Symbol symbol;
MessageID msgId;
if (boundExpression.Kind == BoundKind.FieldAccess)
{
msgId = MessageID.IDS_SK_FIELD;
symbol = ((BoundFieldAccess)boundExpression).FieldSymbol;
}
else
{
msgId = MessageID.IDS_SK_PROPERTY;
symbol = ((BoundPropertyAccess)boundExpression).PropertySymbol;
}
diagnostics.Add(
ErrorCode.ERR_BadSKknown,
node.Location,
methodName,
msgId.Localize(),
MessageID.IDS_SK_METHOD.Localize());
return BadExpression(node, LookupResultKind.Empty, ImmutableArray.Create(symbol), args.Add(receiver));
}
boundExpression = CheckValue(boundExpression, BindValueKind.RValueOrMethodGroup, diagnostics);
boundExpression.WasCompilerGenerated = true;
var analyzedArguments = AnalyzedArguments.GetInstance();
Debug.Assert(!args.Any(e => e.Kind == BoundKind.OutVariablePendingInference ||
e.Kind == BoundKind.OutDeconstructVarPendingInference ||
e.Kind == BoundKind.DiscardExpression && !e.HasExpressionType()));
analyzedArguments.Arguments.AddRange(args);
BoundExpression result = BindInvocationExpression(
node, node, methodName, boundExpression, analyzedArguments, diagnostics, queryClause,
allowUnexpandedForm: allowUnexpandedForm);
// Query operator can't be called dynamically.
if (queryClause != null && result.Kind == BoundKind.DynamicInvocation)
{
// the error has already been reported by BindInvocationExpression
Debug.Assert(diagnostics.HasAnyErrors());
result = CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments);
}
result.WasCompilerGenerated = true;
analyzedArguments.Free();
return result;
}
///
/// Bind an expression as a method invocation.
///
private BoundExpression BindInvocationExpression(
InvocationExpressionSyntax node,
DiagnosticBag diagnostics)
{
BoundExpression result;
if (TryBindNameofOperator(node, diagnostics, out result))
{
return result; // all of the binding is done by BindNameofOperator
}
// M(__arglist()) is legal, but M(__arglist(__arglist()) is not!
bool isArglist = node.Expression.Kind() == SyntaxKind.ArgListExpression;
AnalyzedArguments analyzedArguments = AnalyzedArguments.GetInstance();
if (isArglist)
{
BindArgumentsAndNames(node.ArgumentList, diagnostics, analyzedArguments, allowArglist: false);
result = BindArgListOperator(node, diagnostics, analyzedArguments);
}
else
{
BoundExpression boundExpression = BindMethodGroup(node.Expression, invoked: true, indexed: false, diagnostics: diagnostics);
boundExpression = CheckValue(boundExpression, BindValueKind.RValueOrMethodGroup, diagnostics);
string name = boundExpression.Kind == BoundKind.MethodGroup ? GetName(node.Expression) : null;
BindArgumentsAndNames(node.ArgumentList, diagnostics, analyzedArguments, allowArglist: true);
result = BindInvocationExpression(node, node.Expression, name, boundExpression, analyzedArguments, diagnostics);
}
analyzedArguments.Free();
return result;
}
private BoundExpression BindArgListOperator(InvocationExpressionSyntax node, DiagnosticBag diagnostics, AnalyzedArguments analyzedArguments)
{
// We allow names, oddly enough; M(__arglist(x : 123)) is legal. We just ignore them.
TypeSymbol objType = GetSpecialType(SpecialType.System_Object, diagnostics, node);
for (int i = 0; i < analyzedArguments.Arguments.Count; ++i)
{
BoundExpression argument = analyzedArguments.Arguments[i];
if ((object)argument.Type == null && !argument.HasAnyErrors)
{
// We are going to need every argument in here to have a type. If we don't have one,
// try converting it to object. We'll either succeed (if it is a null literal)
// or fail with a good error message.
//
// Note that the native compiler converts null literals to object, and for everything
// else it either crashes, or produces nonsense code. Roslyn improves upon this considerably.
analyzedArguments.Arguments[i] = GenerateConversionForAssignment(objType, argument, diagnostics);
}
else if (argument.Type.SpecialType == SpecialType.System_Void)
{
Error(diagnostics, ErrorCode.ERR_CantUseVoidInArglist, argument.Syntax);
}
}
ImmutableArray arguments = analyzedArguments.Arguments.ToImmutable();
ImmutableArray refKinds = analyzedArguments.RefKinds.ToImmutableOrNull();
return new BoundArgListOperator(node, arguments, refKinds, null, analyzedArguments.HasErrors);
}
///
/// Bind an expression as a method invocation.
///
private BoundExpression BindInvocationExpression(
SyntaxNode node,
SyntaxNode expression,
string methodName,
BoundExpression boundExpression,
AnalyzedArguments analyzedArguments,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause = null,
bool allowUnexpandedForm = true)
{
BoundExpression result;
NamedTypeSymbol delegateType;
if ((object)boundExpression.Type != null && boundExpression.Type.IsDynamic())
{
// Either we have a dynamic method group invocation "dyn.M(...)" or
// a dynamic delegate invocation "dyn(...)" -- either way, bind it as a dynamic
// invocation and let the lowering pass sort it out.
result = BindDynamicInvocation(node, boundExpression, analyzedArguments, ImmutableArray.Empty, diagnostics, queryClause);
}
else if (boundExpression.Kind == BoundKind.MethodGroup)
{
result = BindMethodGroupInvocation(node, expression, methodName, (BoundMethodGroup)boundExpression, analyzedArguments, diagnostics, queryClause, allowUnexpandedForm: allowUnexpandedForm);
}
else if ((object)(delegateType = GetDelegateType(boundExpression)) != null)
{
if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, delegateType, node: node))
{
return CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments);
}
result = BindDelegateInvocation(node, expression, methodName, boundExpression, analyzedArguments, diagnostics, queryClause, delegateType);
}
else
{
if (!boundExpression.HasAnyErrors)
{
diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_MethodNameExpected), expression.Location);
}
result = CreateBadCall(node, boundExpression, LookupResultKind.NotInvocable, analyzedArguments);
}
CheckRestrictedTypeReceiver(result, this.Compilation, diagnostics);
return result;
}
private BoundExpression BindDynamicInvocation(
SyntaxNode node,
BoundExpression expression,
AnalyzedArguments arguments,
ImmutableArray applicableMethods,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause)
{
CheckNamedArgumentsForDynamicInvocation(arguments, diagnostics);
bool hasErrors = false;
if (expression.Kind == BoundKind.MethodGroup)
{
BoundMethodGroup methodGroup = (BoundMethodGroup)expression;
BoundExpression receiver = methodGroup.ReceiverOpt;
// receiver is null if we are calling a static method declared on an outer class via its simple name:
if (receiver != null)
{
switch (receiver.Kind)
{
case BoundKind.BaseReference:
Error(diagnostics, ErrorCode.ERR_NoDynamicPhantomOnBase, node, methodGroup.Name);
hasErrors = true;
break;
case BoundKind.ThisReference:
// Can't call the HasThis method due to EE doing odd things with containing member and its containing type.
if ((InConstructorInitializer || InFieldInitializer) && receiver.WasCompilerGenerated)
{
// Only a static method can be called in a constructor initializer. If we were not in a ctor initializer
// the runtime binder would ignore the receiver, but in a ctor initializer we can't read "this" before
// the base constructor is called. We need to handle this as a type qualified static method call.
// Also applicable to things like field initializers, which run before the ctor initializer.
expression = methodGroup.Update(
methodGroup.TypeArgumentsOpt,
methodGroup.Name,
methodGroup.Methods,
methodGroup.LookupSymbolOpt,
methodGroup.LookupError,
methodGroup.Flags & ~BoundMethodGroupFlags.HasImplicitReceiver,
receiverOpt: new BoundTypeExpression(node, null, this.ContainingType).MakeCompilerGenerated(),
resultKind: methodGroup.ResultKind);
}
break;
case BoundKind.TypeOrValueExpression:
var typeOrValue = (BoundTypeOrValueExpression)receiver;
// Unfortunately, the runtime binder doesn't have APIs that would allow us to pass both "type or value".
// Ideally the runtime binder would choose between type and value based on the result of the overload resolution.
// We need to pick one or the other here. Dev11 compiler passes the type only if the value can't be accessed.
bool inStaticContext;
bool useType = IsInstance(typeOrValue.Data.ValueSymbol) && !HasThis(isExplicit: false, inStaticContext: out inStaticContext);
BoundExpression finalReceiver = ReplaceTypeOrValueReceiver(typeOrValue, useType, diagnostics);
expression = methodGroup.Update(
methodGroup.TypeArgumentsOpt,
methodGroup.Name,
methodGroup.Methods,
methodGroup.LookupSymbolOpt,
methodGroup.LookupError,
methodGroup.Flags,
finalReceiver,
methodGroup.ResultKind);
break;
}
}
}
ImmutableArray argArray = BuildArgumentsForDynamicInvocation(arguments, diagnostics);
hasErrors &= ReportBadDynamicArguments(node, argArray, diagnostics, queryClause);
return new BoundDynamicInvocation(
node,
expression,
argArray,
arguments.GetNames(),
arguments.RefKinds.ToImmutableOrNull(),
applicableMethods,
type: Compilation.DynamicType,
hasErrors: hasErrors);
}
private void CheckNamedArgumentsForDynamicInvocation(AnalyzedArguments arguments, DiagnosticBag diagnostics)
{
if (arguments.Names.Count == 0)
{
return;
}
if (!Compilation.LanguageVersion.AllowNonTrailingNamedArguments())
{
return;
}
bool seenName = false;
for (int i = 0; i < arguments.Names.Count; i++)
{
if (arguments.Names[i] != null)
{
seenName = true;
}
else if (seenName)
{
Error(diagnostics, ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgumentInDynamicInvocation, arguments.Arguments[i].Syntax);
return;
}
}
}
private ImmutableArray BuildArgumentsForDynamicInvocation(AnalyzedArguments arguments, DiagnosticBag diagnostics)
{
for (int i = 0; i < arguments.Arguments.Count; i++)
{
Debug.Assert(arguments.Arguments[i].Kind != BoundKind.OutDeconstructVarPendingInference);
if (arguments.Arguments[i].Kind == BoundKind.OutVariablePendingInference ||
arguments.Arguments[i].Kind == BoundKind.DiscardExpression && !arguments.Arguments[i].HasExpressionType())
{
var builder = ArrayBuilder.GetInstance(arguments.Arguments.Count);
builder.AddRange(arguments.Arguments);
do
{
BoundExpression argument = builder[i];
if (argument.Kind == BoundKind.OutVariablePendingInference)
{
builder[i] = ((OutVariablePendingInference)argument).FailInference(this, diagnostics);
}
else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType())
{
builder[i] = ((BoundDiscardExpression)argument).FailInference(this, diagnostics);
}
i++;
}
while (i < builder.Count);
return builder.ToImmutableAndFree();
}
}
return arguments.Arguments.ToImmutable();
}
// Returns true if there were errors.
private static bool ReportBadDynamicArguments(
SyntaxNode node,
ImmutableArray arguments,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause)
{
bool hasErrors = false;
bool reportedBadQuery = false;
foreach (var arg in arguments)
{
if (!IsLegalDynamicOperand(arg))
{
if (queryClause != null && !reportedBadQuery)
{
reportedBadQuery = true;
Error(diagnostics, ErrorCode.ERR_BadDynamicQuery, node);
hasErrors = true;
continue;
}
if (arg.Kind == BoundKind.Lambda || arg.Kind == BoundKind.UnboundLambda)
{
// Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgLambda, arg.Syntax);
hasErrors = true;
}
else if (arg.Kind == BoundKind.MethodGroup)
{
// Cannot use a method group as an argument to a dynamically dispatched operation. Did you intend to invoke the method?
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgMemgrp, arg.Syntax);
hasErrors = true;
}
else if (arg.Kind == BoundKind.ArgListOperator)
{
// Not a great error message, since __arglist is not a type, but it'll do.
// error CS1978: Cannot use an expression of type '__arglist' as an argument to a dynamically dispatched operation
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, "__arglist");
}
else if (arg.IsLiteralDefault())
{
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArgDefaultLiteral, arg.Syntax);
hasErrors = true;
}
else
{
// Lambdas,anonymous methods and method groups are the typeless expressions that
// are not usable as dynamic arguments; if we get here then the expression must have a type.
Debug.Assert((object)arg.Type != null);
// error CS1978: Cannot use an expression of type 'int*' as an argument to a dynamically dispatched operation
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, arg.Type);
hasErrors = true;
}
}
}
return hasErrors;
}
private BoundExpression BindDelegateInvocation(
SyntaxNode node,
SyntaxNode expression,
string methodName,
BoundExpression boundExpression,
AnalyzedArguments analyzedArguments,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause,
NamedTypeSymbol delegateType)
{
BoundExpression result;
var methodGroup = MethodGroup.GetInstance();
methodGroup.PopulateWithSingleMethod(boundExpression, delegateType.DelegateInvokeMethod);
var overloadResolutionResult = OverloadResolutionResult.GetInstance();
HashSet useSiteDiagnostics = null;
OverloadResolution.MethodInvocationOverloadResolution(methodGroup.Methods, methodGroup.TypeArguments, analyzedArguments, overloadResolutionResult, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);
// If overload resolution on the "Invoke" method found an applicable candidate, and one of the arguments
// was dynamic then treat this as a dynamic call.
if (analyzedArguments.HasDynamicArgument && overloadResolutionResult.HasAnyApplicableMember)
{
result = BindDynamicInvocation(node, boundExpression, analyzedArguments, overloadResolutionResult.GetAllApplicableMembers(), diagnostics, queryClause);
}
else
{
result = BindInvocationExpressionContinued(node, expression, methodName, overloadResolutionResult, analyzedArguments, methodGroup, delegateType, diagnostics, queryClause);
}
overloadResolutionResult.Free();
methodGroup.Free();
return result;
}
private static bool HasApplicableConditionalMethod(OverloadResolutionResult results)
{
var r = results.Results;
for (int i = 0; i < r.Length; ++i)
{
if (r[i].IsApplicable && r[i].Member.IsConditional)
{
return true;
}
}
return false;
}
private BoundExpression BindMethodGroupInvocation(
SyntaxNode syntax,
SyntaxNode expression,
string methodName,
BoundMethodGroup methodGroup,
AnalyzedArguments analyzedArguments,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause,
bool allowUnexpandedForm = true)
{
BoundExpression result;
HashSet useSiteDiagnostics = null;
var resolution = this.ResolveMethodGroup(
methodGroup, expression, methodName, analyzedArguments, isMethodGroupConversion: false,
useSiteDiagnostics: ref useSiteDiagnostics, allowUnexpandedForm: allowUnexpandedForm);
diagnostics.Add(expression, useSiteDiagnostics);
if (!methodGroup.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading.
if (resolution.HasAnyErrors)
{
ImmutableArray originalMethods;
LookupResultKind resultKind;
ImmutableArray typeArguments;
if (resolution.OverloadResolutionResult != null)
{
originalMethods = GetOriginalMethods(resolution.OverloadResolutionResult);
resultKind = resolution.MethodGroup.ResultKind;
typeArguments = resolution.MethodGroup.TypeArguments.ToImmutable();
}
else
{
originalMethods = methodGroup.Methods;
resultKind = methodGroup.ResultKind;
typeArguments = methodGroup.TypeArgumentsOpt;
}
result = CreateBadCall(
syntax,
methodName,
methodGroup.ReceiverOpt,
originalMethods,
resultKind,
typeArguments,
analyzedArguments,
invokedAsExtensionMethod: resolution.IsExtensionMethodGroup,
isDelegate: false);
}
else if (!resolution.IsEmpty)
{
// We're checking resolution.ResultKind, rather than methodGroup.HasErrors
// to better handle the case where there's a problem with the receiver
// (e.g. inaccessible), but the method group resolved correctly (e.g. because
// it's actually an accessible static method on a base type).
// CONSIDER: could check for error types amongst method group type arguments.
if (resolution.ResultKind != LookupResultKind.Viable)
{
if (resolution.MethodGroup != null)
{
// we want to force any unbound lambda arguments to cache an appropriate conversion if possible; see 9448.
DiagnosticBag discarded = DiagnosticBag.GetInstance();
result = BindInvocationExpressionContinued(
syntax, expression, methodName, resolution.OverloadResolutionResult, resolution.AnalyzedArguments,
resolution.MethodGroup, delegateTypeOpt: null, diagnostics: discarded, queryClause: queryClause);
discarded.Free();
}
// Since the resolution is non-empty and has no diagnostics, the LookupResultKind in its MethodGroup is uninteresting.
result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
}
else
{
// If overload resolution found one or more applicable methods and at least one argument
// was dynamic then treat this as a dynamic call.
if (resolution.AnalyzedArguments.HasDynamicArgument &&
resolution.OverloadResolutionResult.HasAnyApplicableMember)
{
if (resolution.IsLocalFunctionInvocation)
{
result = BindLocalFunctionInvocationWithDynamicArgument(
syntax, expression, methodName, methodGroup,
diagnostics, queryClause, resolution);
}
else if (resolution.IsExtensionMethodGroup)
{
// error CS1973: 'T' has no applicable method named 'M' but appears to have an
// extension method by that name. Extension methods cannot be dynamically dispatched. Consider
// casting the dynamic arguments or calling the extension method without the extension method
// syntax.
// We found an extension method, so the instance associated with the method group must have
// existed and had a type.
Debug.Assert(methodGroup.InstanceOpt != null && (object)methodGroup.InstanceOpt.Type != null);
Error(diagnostics, ErrorCode.ERR_BadArgTypeDynamicExtension, syntax, methodGroup.InstanceOpt.Type, methodGroup.Name);
result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
}
else
{
if (HasApplicableConditionalMethod(resolution.OverloadResolutionResult))
{
// warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime
// because one or more applicable overloads are conditional methods
Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name);
}
// Note that the runtime binder may consider candidates that haven't passed compile-time final validation
// and an ambiguity error may be reported. Also additional checks are performed in runtime final validation
// that are not performed at compile-time.
// Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime.
var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult,
methodGroup.ReceiverOpt,
methodGroup.TypeArgumentsOpt,
diagnostics);
if (finalApplicableCandidates.Length > 0)
{
result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates, diagnostics, queryClause);
}
else
{
result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
}
}
}
else
{
result = BindInvocationExpressionContinued(
syntax, expression, methodName, resolution.OverloadResolutionResult, resolution.AnalyzedArguments,
resolution.MethodGroup, delegateTypeOpt: null, diagnostics: diagnostics, queryClause: queryClause);
}
}
}
else
{
result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
}
resolution.Free();
return result;
}
private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
SyntaxNode syntax,
SyntaxNode expression,
string methodName,
BoundMethodGroup boundMethodGroup,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause,
MethodGroupResolution resolution)
{
// Invocations of local functions with dynamic arguments don't need
// to be dispatched as dynamic invocations since they cannot be
// overloaded. Instead, we'll just emit a standard call with
// dynamic implicit conversions for any dynamic arguments. There
// are two exceptions: "params", and unconstructed generics. While
// implementing those cases with dynamic invocations is possible,
// we have decided the implementation complexity is not worth it.
// Refer to the comments below for the exact semantics.
Debug.Assert(resolution.IsLocalFunctionInvocation);
Debug.Assert(resolution.OverloadResolutionResult.Succeeded);
Debug.Assert(queryClause == null);
var validResult = resolution.OverloadResolutionResult.ValidResult;
var args = resolution.AnalyzedArguments.Arguments.ToImmutable();
ReportBadDynamicArguments(syntax, args, diagnostics, queryClause);
var localFunction = validResult.Member;
var methodResult = validResult.Result;
// We're only in trouble if a dynamic argument is passed to the
// params parameter and is ambiguous at compile time between normal
// and expanded form i.e., there is exactly one dynamic argument to
// a params parameter
// See https://github.com/dotnet/roslyn/issues/10708
if (OverloadResolution.IsValidParams(localFunction) &&
methodResult.Kind == MemberResolutionKind.ApplicableInNormalForm)
{
var parameters = localFunction.Parameters;
Debug.Assert(parameters.Last().IsParams);
var lastParamIndex = parameters.Length - 1;
for (int i = 0; i < args.Length; ++i)
{
var arg = args[i];
if (arg.HasDynamicType() &&
methodResult.ParameterFromArgument(i) == lastParamIndex)
{
Error(diagnostics,
ErrorCode.ERR_DynamicLocalFunctionParamsParameter,
syntax, parameters.Last().Name, localFunction.Name);
return BindDynamicInvocation(
syntax,
boundMethodGroup,
resolution.AnalyzedArguments,
resolution.OverloadResolutionResult.GetAllApplicableMembers(),
diagnostics,
queryClause);
}
}
}
// If we call an unconstructed generic local function with a
// dynamic argument in a place where it influences the type
// parameters, we need to dynamically dispatch the call (as the
// function must be constructed at runtime). We cannot do that, so
// disallow that. However, doing a specific analysis of each
// argument and its corresponding parameter to check if it's
// generic (and allow dynamic in non-generic parameters) may break
// overload resolution in the future, if we ever allow overloaded
// local functions. So, just disallow any mixing of dynamic and
// inferred generics. (Explicit generic arguments are fine)
// See https://github.com/dotnet/roslyn/issues/21317
if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && localFunction.IsGenericMethod)
{
Error(diagnostics,
ErrorCode.ERR_DynamicLocalFunctionTypeParameter,
syntax, localFunction.Name);
return BindDynamicInvocation(
syntax,
boundMethodGroup,
resolution.AnalyzedArguments,
resolution.OverloadResolutionResult.GetAllApplicableMembers(),
diagnostics,
queryClause);
}
return BindInvocationExpressionContinued(
node: syntax,
expression: expression,
methodName: methodName,
result: resolution.OverloadResolutionResult,
analyzedArguments: resolution.AnalyzedArguments,
methodGroup: resolution.MethodGroup,
delegateTypeOpt: null,
diagnostics: diagnostics,
queryClause: queryClause);
}
private ImmutableArray GetCandidatesPassingFinalValidation(
SyntaxNode syntax,
OverloadResolutionResult overloadResolutionResult,
BoundExpression receiverOpt,
ImmutableArray typeArgumentsOpt,
DiagnosticBag diagnostics) where TMethodOrPropertySymbol : Symbol
{
Debug.Assert(overloadResolutionResult.HasAnyApplicableMember);
var finalCandidates = ArrayBuilder.GetInstance();
DiagnosticBag firstFailed = null;
DiagnosticBag candidateDiagnostics = DiagnosticBag.GetInstance();
for (int i = 0, n = overloadResolutionResult.ResultsBuilder.Count; i < n; i++)
{
var result = overloadResolutionResult.ResultsBuilder[i];
if (result.Result.IsApplicable)
{
// For F to pass the check, all of the following must hold:
// ...
// * If the type parameters of F were substituted in the step above, their constraints are satisfied.
// * If F is a static method, the method group must have resulted from a simple-name, a member-access through a type,
// or a member-access whose receiver can't be classified as a type or value until after overload resolution (see §7.6.4.1).
// * If F is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value,
// or a member-access whose receiver can't be classified as a type or value until after overload resolution (see §7.6.4.1).
if (!MemberGroupFinalValidationAccessibilityChecks(receiverOpt, result.Member, syntax, candidateDiagnostics, invokedAsExtensionMethod: false) &&
(typeArgumentsOpt.IsDefault || ((MethodSymbol)(object)result.Member).CheckConstraints(this.Conversions, syntax, this.Compilation, candidateDiagnostics)))
{
finalCandidates.Add(result.Member);
continue;
}
if (firstFailed == null)
{
firstFailed = candidateDiagnostics;
candidateDiagnostics = DiagnosticBag.GetInstance();
}
else
{
candidateDiagnostics.Clear();
}
}
}
if (firstFailed != null)
{
// Report diagnostics of the first candidate that failed the validation
// unless we have at least one candidate that passes.
if (finalCandidates.Count == 0)
{
diagnostics.AddRange(firstFailed);
}
firstFailed.Free();
}
candidateDiagnostics.Free();
return finalCandidates.ToImmutableAndFree();
}
private static void CheckRestrictedTypeReceiver(BoundExpression expression, Compilation compilation, DiagnosticBag diagnostics)
{
Debug.Assert(diagnostics != null);
// It is never legal to box a restricted type, even if we are boxing it as the receiver
// of a method call. When must be box? We skip boxing when the method in question is defined
// on the restricted type or overridden by the restricted type.
switch (expression.Kind)
{
case BoundKind.Call:
{
var call = (BoundCall)expression;
if (!call.HasAnyErrors &&
call.ReceiverOpt != null &&
(object)call.ReceiverOpt.Type != null &&
call.ReceiverOpt.Type.IsRestrictedType() &&
call.Method.ContainingType != call.ReceiverOpt.Type)
{
// error CS0029: Cannot implicitly convert type 'TypedReference' to 'object'
SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, call.ReceiverOpt.Type, call.Method.ContainingType);
Error(diagnostics, ErrorCode.ERR_NoImplicitConv, call.ReceiverOpt.Syntax, distinguisher.First, distinguisher.Second);
}
}
break;
case BoundKind.DynamicInvocation:
{
var dynInvoke = (BoundDynamicInvocation)expression;
if (!dynInvoke.HasAnyErrors &&
(object)dynInvoke.Expression.Type != null &&
dynInvoke.Expression.Type.IsRestrictedType())
{
// eg: b = typedReference.Equals(dyn);
// error CS1978: Cannot use an expression of type 'TypedReference' as an argument to a dynamically dispatched operation
Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, dynInvoke.Expression.Syntax, dynInvoke.Expression.Type);
}
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(expression.Kind);
}
}
///
/// Perform overload resolution on the method group or expression (BoundMethodGroup)
/// and arguments and return a BoundExpression representing the invocation.
///
/// Invocation syntax node.
/// The syntax for the invoked method, including receiver.
/// Name of the invoked method.
/// Overload resolution result for method group executed by caller.
/// Arguments bound by the caller.
/// Method group if the invocation represents a potentially overloaded member.
/// Delegate type if method group represents a delegate.
/// Diagnostics.
/// The syntax for the query clause generating this invocation expression, if any.
/// BoundCall or error expression representing the invocation.
private BoundCall BindInvocationExpressionContinued(
SyntaxNode node,
SyntaxNode expression,
string methodName,
OverloadResolutionResult result,
AnalyzedArguments analyzedArguments,
MethodGroup methodGroup,
NamedTypeSymbol delegateTypeOpt,
DiagnosticBag diagnostics,
CSharpSyntaxNode queryClause = null)
{
Debug.Assert(node != null);
Debug.Assert(methodGroup != null);
Debug.Assert(methodGroup.Error == null);
Debug.Assert(methodGroup.Methods.Count > 0);
Debug.Assert(((object)delegateTypeOpt == null) || (methodGroup.Methods.Count == 1));
var invokedAsExtensionMethod = methodGroup.IsExtensionMethodGroup;
// Delegate invocations should never be considered extension method
// invocations (even though the delegate may refer to an extension method).
Debug.Assert(!invokedAsExtensionMethod || ((object)delegateTypeOpt == null));
// We have already determined that we are not in a situation where we can successfully do
// a dynamic binding. We might be in one of the following situations:
//
// * There were dynamic arguments but overload resolution still found zero applicable candidates.
// * There were no dynamic arguments and overload resolution found zero applicable candidates.
// * There were no dynamic arguments and overload resolution found multiple applicable candidates
// without being able to find the best one.
//
// In those three situations we might give an additional error.
if (!result.Succeeded)
{
if (analyzedArguments.HasErrors)
{
// Errors for arguments have already been reported, except for unbound lambdas.
// We report those now.
foreach (var argument in analyzedArguments.Arguments)
{
var unboundLambda = argument as UnboundLambda;
if (unboundLambda != null)
{
var boundWithErrors = unboundLambda.BindForErrorRecovery();
diagnostics.AddRange(boundWithErrors.Diagnostics);
}
}
}
else
{
// Since there were no argument errors to report, we report an error on the invocation itself.
string name = (object)delegateTypeOpt == null ? methodName : null;
result.ReportDiagnostics(this, GetLocationForOverloadResolutionDiagnostic(node, expression), diagnostics, name,
methodGroup.Receiver, analyzedArguments, methodGroup.Methods.ToImmutable(),
typeContainingConstructor: null, delegateTypeBeingInvoked: delegateTypeOpt,
queryClause: queryClause);
}
return CreateBadCall(node, methodGroup.Name, invokedAsExtensionMethod && analyzedArguments.Arguments.Count > 0 && (object)methodGroup.Receiver == (object)analyzedArguments.Arguments[0] ? null : methodGroup.Receiver,
GetOriginalMethods(result), methodGroup.ResultKind, methodGroup.TypeArguments.ToImmutable(), analyzedArguments, invokedAsExtensionMethod: invokedAsExtensionMethod, isDelegate: ((object)delegateTypeOpt != null));
}
// Otherwise, there were no dynamic arguments and overload resolution found a unique best candidate.
// We still have to determine if it passes final validation.
var methodResult = result.ValidResult;
var returnType = methodResult.Member.ReturnType;
this.CoerceArguments(methodResult, analyzedArguments.Arguments, diagnostics);
var method = methodResult.Member;
var expanded = methodResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm;
var argsToParams = methodResult.Result.ArgsToParamsOpt;
// It is possible that overload resolution succeeded, but we have chosen an
// instance method and we're in a static method. A careful reading of the
// overload resolution spec shows that the "final validation" stage allows an
// "implicit this" on any method call, not just method calls from inside
// instance methods. Therefore we must detect this scenario here, rather than in
// overload resolution.
var receiver = ReplaceTypeOrValueReceiver(methodGroup.Receiver, method.IsStatic && !invokedAsExtensionMethod, diagnostics);
// Note: we specifically want to do final validation (7.6.5.1) without checking delegate compatibility (15.2),
// so we're calling MethodGroupFinalValidation directly, rather than via MethodGroupConversionHasErrors.
// Note: final validation wants the receiver that corresponds to the source representation
// (i.e. the first argument, if invokedAsExtensionMethod).
var gotError = MemberGroupFinalValidation(receiver, method, expression, diagnostics, invokedAsExtensionMethod);
ImmutableArray args;
if (invokedAsExtensionMethod)
{
BoundExpression receiverArgument;
ParameterSymbol receiverParameter = method.Parameters.First();
if ((object)receiver != methodGroup.Receiver)
{
// Because the receiver didn't pass through CoerceArguments, we need to apply an appropriate conversion here.
Debug.Assert(argsToParams.IsDefault || argsToParams[0] == 0);
receiverArgument = CreateConversion(receiver, methodResult.Result.ConversionForArg(0), receiverParameter.Type, diagnostics);
}
else
{
receiverArgument = analyzedArguments.Argument(0);
}
if (receiverParameter.RefKind == RefKind.Ref)
{
// If this was a ref extension method, receiverArgument must be checked for L-value constraints.
// This helper method will also replace it with a BoundBadExpression if it was invalid.
receiverArgument = CheckValue(receiverArgument, BindValueKind.RefOrOut, diagnostics);
CheckFeatureAvailability(receiverArgument.Syntax, MessageID.IDS_FeatureRefExtensionMethods, diagnostics);
}
else if (receiverParameter.RefKind == RefKind.In)
{
CheckFeatureAvailability(receiverArgument.Syntax, MessageID.IDS_FeatureRefExtensionMethods, diagnostics);
}
ArrayBuilder builder = ArrayBuilder.GetInstance(analyzedArguments.Arguments.Count);
builder.Add(receiverArgument);
builder.AddRange(analyzedArguments.Arguments.Skip(1));
args = builder.ToImmutableAndFree();
}
else
{
args = analyzedArguments.Arguments.ToImmutable();
}
// This will be the receiver of the BoundCall node that we create.
// For extension methods, there is no receiver because the receiver in source was actually the first argument.
// For instance methods, we may have synthesized an implicit this node. We'll keep it for the emitter.
// For static methods, we may have synthesized a type expression. It serves no purpose, so we'll drop it.
if (invokedAsExtensionMethod || (method.IsStatic && receiver != null && receiver.WasCompilerGenerated))
{
receiver = null;
}
var argNames = analyzedArguments.GetNames();
var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull();
if (!gotError && !method.IsStatic && receiver != null && receiver.Kind == BoundKind.ThisReference && receiver.WasCompilerGenerated)
{
gotError = IsRefOrOutThisParameterCaptured(node, diagnostics);
}
// What if some of the arguments are implicit? Dev10 reports unsafe errors
// if the implied argument would have an unsafe type. We need to check
// the parameters explicitly, since there won't be bound nodes for the implied
// arguments until lowering.
if (method.HasUnsafeParameter())
{
// Don't worry about double reporting (i.e. for both the argument and the parameter)
// because only one unsafe diagnostic is allowed per scope - the others are suppressed.
gotError = ReportUnsafeIfNotAllowed(node, diagnostics) || gotError;
}
bool hasBaseReceiver = receiver != null && receiver.Kind == BoundKind.BaseReference;
ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver);
// No use site errors, but there could be use site warnings.
// If there are any use site warnings, they have already been reported by overload resolution.
Debug.Assert(!method.HasUseSiteError, "Shouldn't have reached this point if there were use site errors.");
if (method.IsRuntimeFinalizer())
{
ErrorCode code = hasBaseReceiver
? ErrorCode.ERR_CallingBaseFinalizeDeprecated
: ErrorCode.ERR_CallingFinalizeDeprecated;
Error(diagnostics, code, node);
gotError = true;
}
Debug.Assert(args.IsDefaultOrEmpty || (object)receiver != (object)args[0]);
gotError |= !CheckInvocationArgMixing(
node,
method,
receiver,
method.Parameters,
args,
argRefKinds,
argsToParams,
this.LocalScopeDepth,
diagnostics);
if ((object)delegateTypeOpt != null)
{
return new BoundCall(node, receiver, method, args, argNames, argRefKinds, isDelegateCall: true,
expanded: expanded, invokedAsExtensionMethod: invokedAsExtensionMethod,
argsToParamsOpt: argsToParams, resultKind: LookupResultKind.Viable, binderOpt: this, type: returnType, hasErrors: gotError);
}
else
{
if ((object)receiver != null && receiver.Kind == BoundKind.BaseReference && method.IsAbstract)
{
Error(diagnostics, ErrorCode.ERR_AbstractBaseCall, node, method);
gotError = true;
}
if (!method.IsStatic)
{
WarnOnAccessOfOffDefault(node.Kind() == SyntaxKind.InvocationExpression ?
((InvocationExpressionSyntax)node).Expression :
node,
receiver,
diagnostics);
}
return new BoundCall(node, receiver, method, args, argNames, argRefKinds, isDelegateCall: false,
expanded: expanded, invokedAsExtensionMethod: invokedAsExtensionMethod,
argsToParamsOpt: argsToParams, resultKind: LookupResultKind.Viable, binderOpt: this, type: returnType, hasErrors: gotError);
}
}
/// Invocation syntax node.
/// The syntax for the invoked method, including receiver.
private static Location GetLocationForOverloadResolutionDiagnostic(SyntaxNode node, SyntaxNode expression)
{
if (node != expression)
{
switch (expression.Kind())
{
case SyntaxKind.QualifiedName:
return ((QualifiedNameSyntax)expression).Right.GetLocation();
case SyntaxKind.SimpleMemberAccessExpression:
case SyntaxKind.PointerMemberAccessExpression:
return ((MemberAccessExpressionSyntax)expression).Name.GetLocation();
}
}
return expression.GetLocation();
}
///
/// Replace a BoundTypeOrValueExpression with a BoundExpression for either a type (if useType is true)
/// or a value (if useType is false). Any other node is unmodified.
///
///
/// Call this once overload resolution has succeeded on the method group of which the BoundTypeOrValueExpression
/// is the receiver. Generally, useType will be true if the chosen method is static and false otherwise.
///
private BoundExpression ReplaceTypeOrValueReceiver(BoundExpression receiver, bool useType, DiagnosticBag diagnostics)
{
if ((object)receiver == null)
{
return null;
}
switch (receiver.Kind)
{
case BoundKind.TypeOrValueExpression:
var typeOrValue = (BoundTypeOrValueExpression)receiver;
if (useType)
{
diagnostics.AddRange(typeOrValue.Data.TypeDiagnostics);
return typeOrValue.Data.TypeExpression;
}
else
{
diagnostics.AddRange(typeOrValue.Data.ValueDiagnostics);
return CheckValue(typeOrValue.Data.ValueExpression, BindValueKind.RValue, diagnostics);
}
case BoundKind.QueryClause:
// a query clause may wrap a TypeOrValueExpression.
var q = (BoundQueryClause)receiver;
var value = q.Value;
var replaced = ReplaceTypeOrValueReceiver(value, useType, diagnostics);
return (value == replaced) ? q : q.Update(replaced, q.DefinedSymbol, q.Operation, q.Cast, q.Binder, q.UnoptimizedForm, q.Type);
}
return receiver;
}
///
/// Return the delegate type if this expression represents a delegate.
///
private static NamedTypeSymbol GetDelegateType(BoundExpression expr)
{
if ((object)expr != null && expr.Kind != BoundKind.TypeExpression)
{
var type = expr.Type as NamedTypeSymbol;
if (((object)type != null) && type.IsDelegateType())
{
return type;
}
}
return null;
}
private BoundCall CreateBadCall(
SyntaxNode node,
string name,
BoundExpression receiver,
ImmutableArray methods,
LookupResultKind resultKind,
ImmutableArray typeArguments,
AnalyzedArguments analyzedArguments,
bool invokedAsExtensionMethod,
bool isDelegate)
{
MethodSymbol method;
ImmutableArray args;
if (!typeArguments.IsDefaultOrEmpty)
{
var constructedMethods = ArrayBuilder.GetInstance();
foreach (var m in methods)
{
constructedMethods.Add(m.ConstructedFrom == m && m.Arity == typeArguments.Length ? m.Construct(typeArguments) : m);
}
methods = constructedMethods.ToImmutableAndFree();
}
if (methods.Length == 1 && !IsUnboundGeneric(methods[0]))
{
method = methods[0];
}
else
{
var returnType = GetCommonTypeOrReturnType(methods) ?? new ExtendedErrorTypeSymbol(this.Compilation, string.Empty, arity: 0, errorInfo: null);
var methodContainer = (object)receiver != null && (object)receiver.Type != null
? receiver.Type
: this.ContainingType;
method = new ErrorMethodSymbol(methodContainer, returnType, name);
}
args = BuildArgumentsForErrorRecovery(analyzedArguments, methods);
var argNames = analyzedArguments.GetNames();
var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull();
return BoundCall.ErrorCall(node, receiver, method, args, argNames, argRefKinds, isDelegate, invokedAsExtensionMethod: invokedAsExtensionMethod, originalMethods: methods, resultKind: resultKind, binder: this);
}
private static bool IsUnboundGeneric(MethodSymbol method)
{
return method.IsGenericMethod && method.ConstructedFrom() == method;
}
// Arbitrary limit on the number of parameter lists from overload
// resolution candidates considered when binding argument types.
// Any additional parameter lists are ignored.
internal const int MaxParameterListsForErrorRecovery = 10;
private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray methods)
{
var parameterListList = ArrayBuilder>.GetInstance();
foreach (var m in methods)
{
if (!IsUnboundGeneric(m) && m.ParameterCount > 0)
{
parameterListList.Add(m.Parameters);
if (parameterListList.Count == MaxParameterListsForErrorRecovery)
{
break;
}
}
}
var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList);
parameterListList.Free();
return result;
}
private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray properties)
{
var parameterListList = ArrayBuilder>.GetInstance();
foreach (var p in properties)
{
if (p.ParameterCount > 0)
{
parameterListList.Add(p.Parameters);
if (parameterListList.Count == MaxParameterListsForErrorRecovery)
{
break;
}
}
}
var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList);
parameterListList.Free();
return result;
}
private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, IEnumerable> parameterListList)
{
// Since the purpose is to bind any unbound arguments, we return early if there are none.
if (!analyzedArguments.Arguments.Any(e => (object)e.Type == null))
{
return analyzedArguments.Arguments.ToImmutable();
}
int argumentCount = analyzedArguments.Arguments.Count;
ArrayBuilder newArguments = ArrayBuilder.GetInstance(argumentCount);
newArguments.AddRange(analyzedArguments.Arguments);
for (int i = 0; i < argumentCount; i++)
{
var argument = newArguments[i];
if ((object)argument.Type != null)
{
continue;
}
switch (argument.Kind)
{
case BoundKind.UnboundLambda:
{
// bind the argument against each applicable parameter
var unboundArgument = (UnboundLambda)argument;
foreach (var parameterList in parameterListList)
{
var parameterType = GetCorrespondingParameterType(analyzedArguments, i, parameterList);
if (parameterType?.Kind == SymbolKind.NamedType &&
(object)parameterType.GetDelegateType() != null)
{
var discarded = unboundArgument.Bind((NamedTypeSymbol)parameterType);
}
}
// replace the unbound lambda with its best inferred bound version
newArguments[i] = unboundArgument.BindForErrorRecovery();
break;
}
case BoundKind.OutVariablePendingInference:
case BoundKind.DiscardExpression:
{
if (argument.HasExpressionType())
{
break;
}
// See if all applicable applicable parameters have the same type
TypeSymbol candidateType = null;
foreach (var parameterList in parameterListList)
{
var parameterType = GetCorrespondingParameterType(analyzedArguments, i, parameterList);
if ((object)parameterType != null)
{
if ((object)candidateType == null)
{
candidateType = parameterType;
}
else if (!candidateType.Equals(parameterType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
{
// type mismatch
candidateType = null;
break;
}
}
}
if (argument.Kind == BoundKind.OutVariablePendingInference)
{
if ((object)candidateType == null)
{
newArguments[i] = ((OutVariablePendingInference)argument).FailInference(this, null);
}
else
{
newArguments[i] = ((OutVariablePendingInference)argument).SetInferredType(candidateType, null);
}
}
else if (argument.Kind == BoundKind.DiscardExpression)
{
if ((object)candidateType == null)
{
newArguments[i] = ((BoundDiscardExpression)argument).FailInference(this, null);
}
else
{
newArguments[i] = ((BoundDiscardExpression)argument).SetInferredType(candidateType);
}
}
break;
}
case BoundKind.OutDeconstructVarPendingInference:
{
newArguments[i] = ((OutDeconstructVarPendingInference)argument).FailInference(this);
break;
}
}
}
return newArguments.ToImmutableAndFree();
}
///
/// Compute the type of the corresponding parameter, if any. This is used to improve error recovery,
/// for bad invocations, not for semantic analysis of correct invocations, so it is a heuristic.
/// If no parameter appears to correspond to the given argument, we return null.
///
/// The analyzed argument list
/// The index of the argument
/// The parameter list to match against
/// The type of the corresponding parameter.
private static TypeSymbol GetCorrespondingParameterType(AnalyzedArguments analyzedArguments, int i, ImmutableArray parameterList)
{
string name = analyzedArguments.Name(i);
if (name != null)
{
// look for a parameter by that name
foreach (var parameter in parameterList)
{
if (parameter.Name == name) return parameter.Type;
}
return null;
}
return (i < parameterList.Length) ? parameterList[i].Type : null;
// CONSIDER: should we handle variable argument lists?
}
///
/// Absent parameter types to bind the arguments, we simply use the arguments provided for error recovery.
///
private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments)
{
return BuildArgumentsForErrorRecovery(analyzedArguments, Enumerable.Empty>());
}
private BoundCall CreateBadCall(
SyntaxNode node,
BoundExpression expr,
LookupResultKind resultKind,
AnalyzedArguments analyzedArguments)
{
TypeSymbol returnType = new ExtendedErrorTypeSymbol(this.Compilation, string.Empty, arity: 0, errorInfo: null);
var methodContainer = expr.Type ?? this.ContainingType;
MethodSymbol method = new ErrorMethodSymbol(methodContainer, returnType, string.Empty);
var args = BuildArgumentsForErrorRecovery(analyzedArguments);
var argNames = analyzedArguments.GetNames();
var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull();
var originalMethods = (expr.Kind == BoundKind.MethodGroup) ? ((BoundMethodGroup)expr).Methods : ImmutableArray.Empty;
return BoundCall.ErrorCall(node, expr, method, args, argNames, argRefKinds, isDelegateCall: false, invokedAsExtensionMethod: false, originalMethods: originalMethods, resultKind: resultKind, binder: this);
}
private static TypeSymbol GetCommonTypeOrReturnType(ImmutableArray members)
where TMember : Symbol
{
TypeSymbol type = null;
for (int i = 0, n = members.Length; i < n; i++)
{
TypeSymbol returnType = members[i].GetTypeOrReturnType();
if ((object)type == null)
{
type = returnType;
}
else if (type != returnType)
{
return null;
}
}
return type;
}
private bool TryBindNameofOperator(InvocationExpressionSyntax node, DiagnosticBag diagnostics, out BoundExpression result)
{
result = null;
if (node.Expression.Kind() != SyntaxKind.IdentifierName ||
((IdentifierNameSyntax)node.Expression).Identifier.ContextualKind() != SyntaxKind.NameOfKeyword ||
node.ArgumentList.Arguments.Count != 1)
{
return false;
}
ArgumentSyntax argument = node.ArgumentList.Arguments[0];
if (argument.NameColon != null || argument.RefOrOutKeyword != default(SyntaxToken) || InvocableNameofInScope())
{
return false;
}
result = BindNameofOperatorInternal(node, diagnostics);
return true;
}
private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax node, DiagnosticBag diagnostics)
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureNameof, diagnostics);
var argument = node.ArgumentList.Arguments[0].Expression;
string name = "";
// We relax the instance-vs-static requirement for top-level member access expressions by creating a NameofBinder binder.
var nameofBinder = new NameofBinder(argument, this);
var boundArgument = nameofBinder.BindExpression(argument, diagnostics);
if (!boundArgument.HasAnyErrors && CheckSyntaxForNameofArgument(argument, out name, diagnostics) && boundArgument.Kind == BoundKind.MethodGroup)
{
var methodGroup = (BoundMethodGroup)boundArgument;
if (!methodGroup.TypeArgumentsOpt.IsDefaultOrEmpty)
{
// method group with type parameters not allowed
diagnostics.Add(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, argument.Location);
}
else
{
nameofBinder.EnsureNameofExpressionSymbols(methodGroup, diagnostics);
}
}
return new BoundNameOfOperator(node, boundArgument, ConstantValue.Create(name), Compilation.GetSpecialType(SpecialType.System_String));
}
private void EnsureNameofExpressionSymbols(BoundMethodGroup methodGroup, DiagnosticBag diagnostics)
{
// Check that the method group contains something applicable. Otherwise error.
HashSet useSiteDiagnostics = null;
var resolution = ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(methodGroup.Syntax, useSiteDiagnostics);
diagnostics.AddRange(resolution.Diagnostics);
if (resolution.IsExtensionMethodGroup)
{
diagnostics.Add(ErrorCode.ERR_NameofExtensionMethod, methodGroup.Syntax.Location);
}
}
///
/// Returns true if syntax form is OK (so no errors were reported)
///
private bool CheckSyntaxForNameofArgument(ExpressionSyntax argument, out string name, DiagnosticBag diagnostics, bool top = true)
{
switch (argument.Kind())
{
case SyntaxKind.IdentifierName:
{
var syntax = (IdentifierNameSyntax)argument;
name = syntax.Identifier.ValueText;
return true;
}
case SyntaxKind.GenericName:
{
var syntax = (GenericNameSyntax)argument;
name = syntax.Identifier.ValueText;
return true;
}
case SyntaxKind.SimpleMemberAccessExpression:
{
var syntax = (MemberAccessExpressionSyntax)argument;
bool ok = true;
switch (syntax.Expression.Kind())
{
case SyntaxKind.BaseExpression:
case SyntaxKind.ThisExpression:
break;
default:
ok = CheckSyntaxForNameofArgument(syntax.Expression, out name, diagnostics, false);
break;
}
name = syntax.Name.Identifier.ValueText;
return ok;
}
case SyntaxKind.AliasQualifiedName:
{
var syntax = (AliasQualifiedNameSyntax)argument;
bool ok = true;
if (top)
{
diagnostics.Add(ErrorCode.ERR_AliasQualifiedNameNotAnExpression, argument.Location);
ok = false;
}
name = syntax.Name.Identifier.ValueText;
return ok;
}
case SyntaxKind.ThisExpression:
case SyntaxKind.BaseExpression:
case SyntaxKind.PredefinedType:
name = "";
if (top) goto default;
return true;
default:
{
var code = top ? ErrorCode.ERR_ExpressionHasNoName : ErrorCode.ERR_SubexpressionNotInNameof;
diagnostics.Add(code, argument.Location);
name = "";
return false;
}
}
}
///
/// Helper method that checks whether there is an invocable 'nameof' in scope.
///
private bool InvocableNameofInScope()
{
var lookupResult = LookupResult.GetInstance();
const LookupOptions options = LookupOptions.AllMethodsOnArityZero | LookupOptions.MustBeInvocableIfMember;
HashSet useSiteDiagnostics = null;
this.LookupSymbolsWithFallback(lookupResult, SyntaxFacts.GetText(SyntaxKind.NameOfKeyword), useSiteDiagnostics: ref useSiteDiagnostics, arity: 0, options: options);
var result = lookupResult.IsMultiViable;
lookupResult.Free();
return result;
}
}
}