public MethodGroupResolution( MethodGroup methodGroup, Symbol otherSymbol, OverloadResolutionResult<MethodSymbol> overloadResolutionResult, AnalyzedArguments analyzedArguments, LookupResultKind resultKind, ImmutableArray<Diagnostic> diagnostics, bool extensionMethodsOfSameViabilityAreAvailable) { Debug.Assert((methodGroup == null) || (methodGroup.Methods.Count > 0)); Debug.Assert((methodGroup == null) || ((object)otherSymbol == null)); // Methods should be represented in the method group. Debug.Assert(((object)otherSymbol == null) || (otherSymbol.Kind != SymbolKind.Method)); Debug.Assert(resultKind != LookupResultKind.Ambiguous); // HasAnyApplicableMethod is expecting Viable methods. Debug.Assert(!diagnostics.IsDefault); Debug.Assert(!extensionMethodsOfSameViabilityAreAvailable || methodGroup == null || !methodGroup.IsExtensionMethodGroup); this.MethodGroup = methodGroup; this.OtherSymbol = otherSymbol; this.OverloadResolutionResult = overloadResolutionResult; this.AnalyzedArguments = analyzedArguments; this.ResultKind = resultKind; this.Diagnostics = diagnostics; this.ExtensionMethodsOfSameViabilityAreAvailable = extensionMethodsOfSameViabilityAreAvailable; }
public MethodGroupResolution( MethodGroup methodGroup, OverloadResolutionResult<MethodSymbol> overloadResolutionResult, AnalyzedArguments analyzedArguments, ImmutableArray<Diagnostic> diagnostics) : this(methodGroup, null, overloadResolutionResult, analyzedArguments, methodGroup.ResultKind, diagnostics) { }
protected BoundExpression MakeConstruction(CSharpSyntaxNode node, NamedTypeSymbol toCreate, ImmutableArray <BoundExpression> args, DiagnosticBag diagnostics) { AnalyzedArguments analyzedArguments = AnalyzedArguments.GetInstance(); analyzedArguments.Arguments.AddRange(args); var result = BindClassCreationExpression(node, toCreate.Name, node, toCreate, analyzedArguments, diagnostics); result.WasCompilerGenerated = true; analyzedArguments.Free(); return(result); }
/// <summary> /// Resolve method group based on the optional delegate invoke method. /// If the invoke method is null, ignore arguments in resolution. /// </summary> private static MethodGroupResolution ResolveDelegateMethodGroup(Binder binder, BoundMethodGroup source, MethodSymbol delegateInvokeMethodOpt, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if ((object)delegateInvokeMethodOpt != null) { var analyzedArguments = new AnalyzedArguments(); GetDelegateArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); var resolution = binder.ResolveMethodGroup(source, analyzedArguments, isMethodGroupConversion: true, inferWithDynamic: true, useSiteDiagnostics: ref useSiteDiagnostics); return(resolution); } else { return(binder.ResolveMethodGroup(source, null, isMethodGroupConversion: true, useSiteDiagnostics: ref useSiteDiagnostics)); } }
/// <summary> /// The overload resolution portion of FindForEachPatternMethod. /// </summary> private MethodSymbol PerformForEachPatternOverloadResolution(TypeSymbol patternType, ArrayBuilder <MethodSymbol> candidateMethods, bool warningsOnly, DiagnosticBag diagnostics) { ArrayBuilder <TypeSymbol> typeArguments = ArrayBuilder <TypeSymbol> .GetInstance(); AnalyzedArguments arguments = AnalyzedArguments.GetInstance(); OverloadResolutionResult <MethodSymbol> overloadResolutionResult = OverloadResolutionResult <MethodSymbol> .GetInstance(); HashSet <DiagnosticInfo> useSiteDiagnostics = null; this.OverloadResolution.MethodInvocationOverloadResolution(candidateMethods, typeArguments, arguments, overloadResolutionResult, ref useSiteDiagnostics); diagnostics.Add(syntax.Expression, useSiteDiagnostics); MethodSymbol result = null; if (overloadResolutionResult.Succeeded) { result = overloadResolutionResult.ValidResult.Member; if (result.IsStatic || result.DeclaredAccessibility != Accessibility.Public) { if (warningsOnly) { diagnostics.Add(ErrorCode.WRN_PatternStaticOrInaccessible, syntax.Expression.Location, patternType, MessageID.IDS_Collection.Localize(), result); } result = null; } else if (result.CallsAreOmitted(syntax.SyntaxTree)) { // Calls to this method are omitted in the current syntax tree, i.e it is either a partial method with no implementation part OR a conditional method whose condition is not true in this source file. // We don't want to want to allow this case, see StatementBinder::bindPatternToMethod. result = null; } } else if (overloadResolutionResult.Results.Length > 1) { if (warningsOnly) { diagnostics.Add(ErrorCode.WRN_PatternIsAmbiguous, syntax.Expression.Location, patternType, MessageID.IDS_Collection.Localize(), overloadResolutionResult.Results[0].Member, overloadResolutionResult.Results[1].Member); } } overloadResolutionResult.Free(); arguments.Free(); typeArguments.Free(); return(result); }
private static ArgumentAnalysisResult AnalyzeArgumentsForNormalFormNoNamedArguments( ImmutableArray <ParameterSymbol> parameters, AnalyzedArguments arguments, bool isMethodGroupConversion, bool isVararg ) { Debug.Assert(!parameters.IsDefault); Debug.Assert(arguments != null); Debug.Assert(arguments.Names.Count == 0); // We simulate an additional non-optional parameter for a vararg method. int parameterCount = parameters.Length + (isVararg ? 1 : 0); int argumentCount = arguments.Arguments.Count; // If there are no named arguments then analyzing the argument and parameter // matching in normal form is simple: each argument corresponds exactly to // the matching parameter, and if there are not enough arguments then the // unmatched parameters had better all be optional. If there are too // few parameters then one of the arguments has no matching parameter. // Otherwise, everything is just right. if (argumentCount < parameterCount) { for ( int parameterPosition = argumentCount; parameterPosition < parameterCount; ++parameterPosition ) { if ( parameters.Length == parameterPosition || !CanBeOptional(parameters[parameterPosition], isMethodGroupConversion) ) { return(ArgumentAnalysisResult.RequiredParameterMissing(parameterPosition)); } } } else if (parameterCount < argumentCount) { return(ArgumentAnalysisResult.NoCorrespondingParameter(parameterCount)); } // A null map means that every argument in the argument list corresponds exactly to // the same position in the formal parameter list. return(ArgumentAnalysisResult.NormalForm(default(ImmutableArray <int>))); }
public Conversion MethodGroupConversion(CSharpSyntaxNode syntax, MethodGroup methodGroup, NamedTypeSymbol delegateType, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { var analyzedArguments = AnalyzedArguments.GetInstance(); var result = OverloadResolutionResult <MethodSymbol> .GetInstance(); Debug.Assert((object)delegateType.DelegateInvokeMethod != null && !delegateType.DelegateInvokeMethod.HasUseSiteError, "This method should only be called for valid delegate types"); GetDelegateArguments(syntax, analyzedArguments, delegateType.DelegateInvokeMethod.Parameters, Compilation); _binder.OverloadResolution.MethodInvocationOverloadResolution( methodGroup.Methods, methodGroup.TypeArguments, analyzedArguments, result, ref useSiteDiagnostics, isMethodGroupConversion: true); var conversion = ToConversion(result, methodGroup, delegateType); analyzedArguments.Free(); result.Free(); return(conversion); }
/// <summary> /// Resolve method group based on the optional delegate invoke method. /// If the invoke method is null, ignore arguments in resolution. /// </summary> private static MethodGroupResolution ResolveDelegateOrFunctionPointerMethodGroup(Binder binder, BoundMethodGroup source, MethodSymbol delegateInvokeMethodOpt, bool isFunctionPointer, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if ((object)delegateInvokeMethodOpt != null) { var analyzedArguments = AnalyzedArguments.GetInstance(); GetDelegateArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); var resolution = binder.ResolveMethodGroup(source, analyzedArguments, useSiteDiagnostics: ref useSiteDiagnostics, inferWithDynamic: true, isMethodGroupConversion: true, returnRefKind: delegateInvokeMethodOpt.RefKind, returnType: delegateInvokeMethodOpt.ReturnType, isFunctionPointerResolution: isFunctionPointer, callingConvention: delegateInvokeMethodOpt.CallingConvention); analyzedArguments.Free(); return(resolution); } else { return(binder.ResolveMethodGroup(source, analyzedArguments: null, isMethodGroupConversion: true, ref useSiteDiagnostics)); } }
private static bool ReportQueryInferenceFailedSelectMany( FromClauseSyntax fromClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray <Symbol> symbols, BindingDiagnosticBag diagnostics ) { Debug.Assert(methodName == "SelectMany"); // Estimate the return type of Select's lambda argument BoundExpression arg = arguments.Argument(arguments.IsExtensionMethodInvocation ? 1 : 0); TypeSymbol type = null; if (arg.Kind == BoundKind.UnboundLambda) { var unbound = (UnboundLambda)arg; foreach (var t in unbound.Data.InferredReturnTypes()) { if (!t.IsErrorType()) { type = t; break; } } } if ((object)type == null || type.IsErrorType()) { return(false); } TypeSymbol receiverType = receiver?.Type; diagnostics.Add( new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryTypeInferenceFailedSelectMany, new object[] { type, receiverType, methodName }, symbols ), fromClause.Expression.Location ); return(true); }
internal void ResolveOverloads <TMember>( ImmutableArray <TMember> members, ImmutableArray <TypeSymbol> typeArguments, ImmutableArray <ArgumentSyntax> arguments, OverloadResolutionResult <TMember> result, ref HashSet <DiagnosticInfo> useSiteDiagnostics, bool allowRefOmittedArguments) where TMember : Symbol { var methodsBuilder = ArrayBuilder <TMember> .GetInstance(members.Length); methodsBuilder.AddRange(members); var typeArgumentsBuilder = ArrayBuilder <TypeSymbol> .GetInstance(typeArguments.Length); typeArgumentsBuilder.AddRange(typeArguments); var analyzedArguments = AnalyzedArguments.GetInstance(); var unusedDiagnostics = DiagnosticBag.GetInstance(); foreach (var argumentSyntax in arguments) { var argument = this.BindExpression(argumentSyntax.Expression, unusedDiagnostics); analyzedArguments.Arguments.Add(argument); analyzedArguments.RefKinds.Add(argumentSyntax.RefOrOutKeyword.CSharpKind().GetRefKind()); analyzedArguments.Names.Add(argumentSyntax.NameColon == null ? null : argumentSyntax.NameColon.Name); } OverloadResolution.MethodOrPropertyOverloadResolution( methodsBuilder, typeArgumentsBuilder, analyzedArguments, result, isMethodGroupConversion: false, allowRefOmittedArguments: allowRefOmittedArguments, useSiteDiagnostics: ref useSiteDiagnostics); methodsBuilder.Free(); typeArgumentsBuilder.Free(); analyzedArguments.Free(); unusedDiagnostics.Free(); }
private static int?CheckForBadNonTrailingNamedArgument( AnalyzedArguments arguments, ParameterMap argsToParameters, ImmutableArray <ParameterSymbol> parameters ) { // Is there any named argument used out-of-position and followed by unnamed arguments? // If the map is trivial then clearly not. if (argsToParameters.IsTrivial) { return(null); } // Find the first named argument which is used out-of-position int foundPosition = -1; int length = arguments.Arguments.Count; for (int i = 0; i < length; i++) { int parameter = argsToParameters[i]; if (parameter != -1 && parameter != i && arguments.Name(i) != null) { foundPosition = i; break; } } if (foundPosition != -1) { // Verify that all the following arguments are named for (int i = foundPosition + 1; i < length; i++) { if (arguments.Name(i) == null) { return(foundPosition); } } } return(null); }
private static int?NameUsedForPositional( AnalyzedArguments arguments, ParameterMap argsToParameters ) { // Was there a named argument used for a previously-supplied positional argument? // If the map is trivial then clearly not. if (argsToParameters.IsTrivial) { return(null); } // PERFORMANCE: This is an O(n-squared) algorithm, but n will typically be small. We could rewrite this // PERFORMANCE: as a linear algorithm if we wanted to allocate more memory. for ( int argumentPosition = 0; argumentPosition < argsToParameters.Length; ++argumentPosition ) { if (arguments.Name(argumentPosition) != null) { for (int i = 0; i < argumentPosition; ++i) { if (arguments.Name(i) == null) { if (argsToParameters[argumentPosition] == argsToParameters[i]) { // Error; we've got a named argument that corresponds to // a previously-given positional argument. return(argumentPosition); } } } } } return(null); }
internal void ResolveOverloads <TMember>( ImmutableArray <TMember> members, ImmutableArray <TypeSymbol> typeArguments, ImmutableArray <ArgumentSyntax> arguments, OverloadResolutionResult <TMember> result, ref HashSet <DiagnosticInfo> useSiteDiagnostics, bool allowRefOmittedArguments) where TMember : Symbol { var methodsBuilder = ArrayBuilder <TMember> .GetInstance(members.Length); methodsBuilder.AddRange(members); var typeArgumentsBuilder = ArrayBuilder <TypeSymbol> .GetInstance(typeArguments.Length); typeArgumentsBuilder.AddRange(typeArguments); var analyzedArguments = AnalyzedArguments.GetInstance(); var unusedDiagnostics = DiagnosticBag.GetInstance(); foreach (var argumentSyntax in arguments) { BindArgumentAndName(analyzedArguments, unusedDiagnostics, false, argumentSyntax, allowArglist: false); } OverloadResolution.MethodOrPropertyOverloadResolution( methodsBuilder, typeArgumentsBuilder, analyzedArguments, result, isMethodGroupConversion: false, allowRefOmittedArguments: allowRefOmittedArguments, useSiteDiagnostics: ref useSiteDiagnostics); methodsBuilder.Free(); typeArgumentsBuilder.Free(); analyzedArguments.Free(); unusedDiagnostics.Free(); }
public MethodGroupResolution( MethodGroup methodGroup, Symbol otherSymbol, OverloadResolutionResult <MethodSymbol> overloadResolutionResult, AnalyzedArguments analyzedArguments, LookupResultKind resultKind, ImmutableArray <Diagnostic> diagnostics) { Debug.Assert((methodGroup == null) || (methodGroup.Methods.Count > 0)); Debug.Assert((methodGroup == null) || ((object)otherSymbol == null)); // Methods should be represented in the method group. Debug.Assert(((object)otherSymbol == null) || (otherSymbol.Kind != SymbolKind.Method)); Debug.Assert(resultKind != LookupResultKind.Ambiguous); // HasAnyApplicableMethod is expecting Viable methods. Debug.Assert(!diagnostics.IsDefault); this.MethodGroup = methodGroup; this.OtherSymbol = otherSymbol; this.OverloadResolutionResult = overloadResolutionResult; this.AnalyzedArguments = analyzedArguments; this.ResultKind = resultKind; this.Diagnostics = diagnostics; }
protected virtual MethodSymbol BindAttributeConstructor( AttributeSyntax node, NamedTypeSymbol attributeType, AnalyzedArguments boundConstructorArguments, DiagnosticBag diagnostics, ref LookupResultKind resultKind, bool suppressErrors, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { MemberResolutionResult<MethodSymbol> memberResolutionResult; ImmutableArray<MethodSymbol> candidateConstructors; if (!TryPerformConstructorOverloadResolution( attributeType, boundConstructorArguments, attributeType.Name, node.Location, suppressErrors, //don't cascade in these cases diagnostics, out memberResolutionResult, out candidateConstructors, allowProtectedConstructorsOfBaseType: true)) { resultKind = resultKind.WorseResultKind( memberResolutionResult.IsValid && !IsConstructorAccessible(memberResolutionResult.Member, ref useSiteDiagnostics) ? LookupResultKind.Inaccessible : LookupResultKind.OverloadResolutionFailure); } return memberResolutionResult.Member; }
private void BindPCallNativeAndDelegate(InvocationExpressionSyntax node, ArrayBuilder <BoundExpression> args, DiagnosticBag diagnostics, TypeSyntax type) { var XNode = node.XNode as XP.MethodCallContext; string method = XNode?.Expr.GetText(); if (string.IsNullOrEmpty(method)) { method = "PCALLNATIVE"; } if (!ValidatePCallArguments(node, args, diagnostics, method)) { return; } // Our parent is the invocation expression of the delegate AnalyzedArguments analyzedArguments = AnalyzedArguments.GetInstance(); try { var ts = FindPCallDelegateType(type as IdentifierNameSyntax); if (ts != null && ts.IsDelegateType()) { SourceDelegateMethodSymbol delmeth = ts.DelegateInvokeMethod() as SourceDelegateMethodSymbol; // create new parameters based on the parameters from out parent call var invoke = node.Parent as InvocationExpressionSyntax; var realargs = invoke.ArgumentList; var delparams = ts.DelegateParameters(); BindArgumentsAndNames(realargs, diagnostics, analyzedArguments); var builder = ArrayBuilder <ParameterSymbol> .GetInstance(); int i = 0; foreach (var expr in analyzedArguments.Arguments) { var ptype = expr.Type; if (ptype == null) { ptype = new PointerTypeSymbol(Compilation.GetSpecialType(SpecialType.System_Void)); } var parameter = new SourceSimpleParameterSymbol( delmeth, ptype, i, delparams[i].RefKind, delparams[i].Name, delparams[i].Locations); builder.Add(parameter); i++; } delmeth.InitializeParameters(builder.ToImmutableAndFree()); } else { Error(diagnostics, ErrorCode.ERR_PCallResolveGeneratedDelegate, node, method, type.ToString()); } return; } finally { analyzedArguments.Free(); } }
internal AnalyzedAttributeArguments(AnalyzedArguments constructorArguments, ImmutableArray<BoundExpression> namedArguments) { this.ConstructorArguments = constructorArguments; this.NamedArguments = namedArguments; }
private static ArgumentAnalysisResult AnalyzeArguments( Symbol symbol, AnalyzedArguments arguments, bool isMethodGroupConversion, bool expanded) { Debug.Assert((object)symbol != null); Debug.Assert(arguments != null); ImmutableArray <ParameterSymbol> parameters = symbol.GetParameters(); bool isVararg = symbol.GetIsVararg(); // The easy out is that we have no named arguments and are in normal form. if (!expanded && arguments.Names.Count == 0) { return(AnalyzeArgumentsForNormalFormNoNamedArguments(parameters, arguments, isMethodGroupConversion, isVararg)); } // We simulate an additional non-optional parameter for a vararg method. int argumentCount = arguments.Arguments.Count; int[] parametersPositions = null; int? unmatchedArgumentIndex = null; bool? unmatchedArgumentIsNamed = null; // Try to map every argument position to a formal parameter position: bool seenNamedParams = false; bool seenOutOfPositionNamedArgument = false; bool isValidParams = IsValidParams(symbol); for (int argumentPosition = 0; argumentPosition < argumentCount; ++argumentPosition) { // We use -1 as a sentinel to mean that no parameter was found that corresponded to this argument. bool isNamedArgument; int parameterPosition = CorrespondsToAnyParameter(parameters, expanded, arguments, argumentPosition, isValidParams, isVararg, out isNamedArgument, ref seenNamedParams, ref seenOutOfPositionNamedArgument) ?? -1; if (parameterPosition == -1 && unmatchedArgumentIndex == null) { unmatchedArgumentIndex = argumentPosition; unmatchedArgumentIsNamed = isNamedArgument; } if (parameterPosition != argumentPosition && parametersPositions == null) { parametersPositions = new int[argumentCount]; for (int i = 0; i < argumentPosition; ++i) { parametersPositions[i] = i; } } if (parametersPositions != null) { parametersPositions[argumentPosition] = parameterPosition; } } ParameterMap argsToParameters = new ParameterMap(parametersPositions, argumentCount); // We have analyzed every argument and tried to make it correspond to a particular parameter. // We must now answer the following questions: // // (1) Is there any named argument used out-of-position and followed by unnamed arguments? // (2) Is there any argument without a corresponding parameter? // (3) Was there any named argument that specified a parameter that was already // supplied with a positional parameter? // (4) Is there any non-optional parameter without a corresponding argument? // (5) Is there any named argument that were specified twice? // // If the answer to any of these questions is "yes" then the method is not applicable. // It is possible that the answer to any number of these questions is "yes", and so // we must decide which error condition to prioritize when reporting the error, // should we need to report why a given method is not applicable. We prioritize // them in the given order. // (1) Is there any named argument used out-of-position and followed by unnamed arguments? int?badNonTrailingNamedArgument = CheckForBadNonTrailingNamedArgument(arguments, argsToParameters, parameters); if (badNonTrailingNamedArgument != null) { return(ArgumentAnalysisResult.BadNonTrailingNamedArgument(badNonTrailingNamedArgument.Value)); } // (2) Is there any argument without a corresponding parameter? if (unmatchedArgumentIndex != null) { if (unmatchedArgumentIsNamed.Value) { return(ArgumentAnalysisResult.NoCorrespondingNamedParameter(unmatchedArgumentIndex.Value)); } else { return(ArgumentAnalysisResult.NoCorrespondingParameter(unmatchedArgumentIndex.Value)); } } // (3) was there any named argument that specified a parameter that was already // supplied with a positional parameter? int?nameUsedForPositional = NameUsedForPositional(arguments, argsToParameters); if (nameUsedForPositional != null) { return(ArgumentAnalysisResult.NameUsedForPositional(nameUsedForPositional.Value)); } // (4) Is there any non-optional parameter without a corresponding argument? int?requiredParameterMissing = CheckForMissingRequiredParameter(argsToParameters, parameters, isMethodGroupConversion, expanded); if (requiredParameterMissing != null) { return(ArgumentAnalysisResult.RequiredParameterMissing(requiredParameterMissing.Value)); } // __arglist cannot be used with named arguments (as it doesn't have a name) if (arguments.Names.Any() && arguments.Names.Last() != null && isVararg) { return(ArgumentAnalysisResult.RequiredParameterMissing(parameters.Length)); } // (5) Is there any named argument that were specified twice? int?duplicateNamedArgument = CheckForDuplicateNamedArgument(arguments); if (duplicateNamedArgument != null) { return(ArgumentAnalysisResult.DuplicateNamedArgument(duplicateNamedArgument.Value)); } // We're good; this one might be applicable in the given form. return(expanded ? ArgumentAnalysisResult.ExpandedForm(argsToParameters.ToImmutableArray()) : ArgumentAnalysisResult.NormalForm(argsToParameters.ToImmutableArray())); }
private void BindPCall(InvocationExpressionSyntax node, DiagnosticBag diagnostics, AnalyzedArguments analyzedArguments) { if (node.XPCall && node.Expression is GenericNameSyntax) { var gns = node.Expression as GenericNameSyntax; var arg = gns.TypeArgumentList.Arguments[0]; var method = arg.ToFullString(); bool pcallnative = method.IndexOf(XSharpSpecialNames.PCallNativePrefix, XSharpString.Comparison) >= 0; if (pcallnative) { BindPCallNativeAndDelegate(node, analyzedArguments.Arguments, diagnostics, arg); } else { BindPCallAndDelegate(node, analyzedArguments.Arguments, diagnostics, arg); } } }
/// <summary> /// Find the Deconstruct method for the expression on the right, that will fit the number of assignable variables on the left. /// Returns an invocation expression if the Deconstruct method is found. /// If so, it outputs placeholders that were coerced to the output types of the resolved Deconstruct method. /// The overload resolution is similar to writing `receiver.Deconstruct(out var x1, out var x2, ...)`. /// </summary> private BoundExpression MakeDeconstructInvocationExpression( int numCheckedVariables, BoundExpression receiver, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, out ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders) { var receiverSyntax = receiver.Syntax; if (receiver.Type.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_CannotDeconstructDynamic, receiverSyntax); outPlaceholders = default(ImmutableArray <BoundDeconstructValuePlaceholder>); return(BadExpression(receiverSyntax, receiver)); } var analyzedArguments = AnalyzedArguments.GetInstance(); var outVars = ArrayBuilder <OutDeconstructVarPendingInference> .GetInstance(numCheckedVariables); DiagnosticBag bag = null; try { for (int i = 0; i < numCheckedVariables; i++) { var variable = new OutDeconstructVarPendingInference(syntax); analyzedArguments.Arguments.Add(variable); analyzedArguments.RefKinds.Add(RefKind.Out); outVars.Add(variable); } const string methodName = "Deconstruct"; var memberAccess = BindInstanceMemberAccess( receiverSyntax, receiverSyntax, receiver, methodName, rightArity: 0, typeArgumentsSyntax: default(SeparatedSyntaxList <TypeSyntax>), typeArguments: default(ImmutableArray <TypeSymbol>), invoked: true, diagnostics: diagnostics); memberAccess = CheckValue(memberAccess, BindValueKind.RValueOrMethodGroup, diagnostics); memberAccess.WasCompilerGenerated = true; if (memberAccess.Kind != BoundKind.MethodGroup) { return(MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, receiver)); } // After the overload resolution completes, the last step is to coerce the arguments with inferred types. // That step returns placeholder (of correct type) instead of the outVar nodes that were passed in as arguments. // So the generated invocation expression will contain placeholders instead of those outVar nodes. // Those placeholders are also recorded in the outVar for easy access below, by the `SetInferredType` call on the outVar nodes. bag = DiagnosticBag.GetInstance(); BoundExpression result = BindMethodGroupInvocation( receiverSyntax, receiverSyntax, methodName, (BoundMethodGroup)memberAccess, analyzedArguments, bag, queryClause: null, allowUnexpandedForm: true); result.WasCompilerGenerated = true; diagnostics.AddRange(bag); if (bag.HasAnyErrors()) { return(MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result)); } // Verify all the parameters (except "this" for extension methods) are out parameters if (result.Kind != BoundKind.Call) { return(MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result)); } var deconstructMethod = ((BoundCall)result).Method; var parameters = deconstructMethod.Parameters; for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++) { if (parameters[i].RefKind != RefKind.Out) { return(MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result)); } } if (outVars.Any(v => (object)v.Placeholder == null)) { return(MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result)); } outPlaceholders = outVars.SelectAsArray(v => v.Placeholder); return(result); } finally { analyzedArguments.Free(); outVars.Free(); if (bag != null) { bag.Free(); } } }
private static int?CorrespondsToAnyParameter( ImmutableArray <ParameterSymbol> memberParameters, bool expanded, AnalyzedArguments arguments, int argumentPosition, bool isValidParams, bool isVararg, out bool isNamedArgument, ref bool seenNamedParams, ref bool seenOutOfPositionNamedArgument) { // Spec 7.5.1.1: Corresponding parameters: // For each argument in an argument list there has to be a corresponding parameter in // the function member or delegate being invoked. The parameter list used in the // following is determined as follows: // - For virtual methods and indexers defined in classes, the parameter list is picked from the most specific // declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes. // - For interface methods and indexers, the parameter list is picked form the most specific definition of the member, // starting with the interface type and searching through the base interfaces. If no unique parameter list is found, // a parameter list with inaccessible names and no optional parameters is constructed, so that invocations cannot use // named parameters or omit optional arguments. // - For partial methods, the parameter list of the defining partial method declaration is used. // - For all other function members and delegates there is only a single parameter list, which is the one used. // // The position of an argument or parameter is defined as the number of arguments or // parameters preceding it in the argument list or parameter list. // // The corresponding parameters for function member arguments are established as follows: // // Arguments in the argument-list of instance constructors, methods, indexers and delegates: isNamedArgument = arguments.Names.Count > argumentPosition && arguments.Names[argumentPosition] != null; if (!isNamedArgument) { // Spec: // - A positional argument where a fixed parameter occurs at the same position in the // parameter list corresponds to that parameter. // - A positional argument of a function member with a parameter array invoked in its // normal form corresponds to the parameter array, which must occur at the same // position in the parameter list. // - A positional argument of a function member with a parameter array invoked in its // expanded form, where no fixed parameter occurs at the same position in the // parameter list, corresponds to an element in the parameter array. if (seenNamedParams) { // Unnamed arguments after a named argument corresponding to a params parameter cannot correspond to any parameters return(null); } if (seenOutOfPositionNamedArgument) { // Unnamed arguments after an out-of-position named argument cannot correspond to any parameters return(null); } int parameterCount = memberParameters.Length + (isVararg ? 1 : 0); if (argumentPosition >= parameterCount) { return(expanded ? parameterCount - 1 : (int?)null); } return(argumentPosition); } else { // SPEC: A named argument corresponds to the parameter of the same name in the parameter list. // SPEC VIOLATION: The intention of this line of the specification, when contrasted with // SPEC VIOLATION: the lines on positional arguments quoted above, was to disallow a named // SPEC VIOLATION: argument from corresponding to an element of a parameter array when // SPEC VIOLATION: the method was invoked in its expanded form. That is to say that in // SPEC VIOLATION: this case: M(params int[] x) ... M(x : 1234); the named argument // SPEC VIOLATION: corresponds to x in the normal form (and is then inapplicable), but // SPEC VIOLATION: the named argument does *not* correspond to a member of params array // SPEC VIOLATION: x in the expanded form. // SPEC VIOLATION: Sadly that is not what we implemented in C# 4, and not what we are // SPEC VIOLATION: implementing here. If you do that, we make x correspond to the // SPEC VIOLATION: parameter array and allow the candidate to be applicable in its // SPEC VIOLATION: expanded form. var name = arguments.Names[argumentPosition]; for (int p = 0; p < memberParameters.Length; ++p) { // p is initialized to zero; it is ok for a named argument to "correspond" to // _any_ parameter (not just the parameters past the point of positional arguments) if (memberParameters[p].Name == name.Identifier.ValueText) { if (isValidParams && p == memberParameters.Length - 1) { seenNamedParams = true; } if (p != argumentPosition) { seenOutOfPositionNamedArgument = true; } return(p); } } } return(null); }
private static int? NameUsedForPositional(AnalyzedArguments arguments, ParameterMap argsToParameters) { // Was there a named argument used for a previously-supplied positional argument? // If the map is trivial then clearly not. if (argsToParameters.IsTrivial) { return null; } // PERFORMANCE: This is an O(n-squared) algorithm, but n will typically be small. We could rewrite this // PERFORMANCE: as a linear algorithm if we wanted to allocate more memory. for (int argumentPosition = 0; argumentPosition < argsToParameters.Length; ++argumentPosition) { if (arguments.Name(argumentPosition) != null) { for (int i = 0; i < argumentPosition; ++i) { if (arguments.Name(i) == null) { if (argsToParameters[argumentPosition] == argsToParameters[i]) { // Error; we've got a named argument that corresponds to // a previously-given positional argument. return argumentPosition; } } } } } return null; }
private static ArgumentAnalysisResult AnalyzeArguments( Symbol symbol, AnalyzedArguments arguments, bool isMethodGroupConversion, bool expanded) { Debug.Assert((object)symbol != null); Debug.Assert(arguments != null); ImmutableArray<ParameterSymbol> parameters = symbol.GetParameters(); // The easy out is that we have no named arguments and are in normal form. if (!expanded && arguments.Names.Count == 0) { return AnalyzeArgumentsForNormalFormNoNamedArguments(parameters, arguments, isMethodGroupConversion, symbol.GetIsVararg()); } // We simulate an additional non-optional parameter for a vararg method. int argumentCount = arguments.Arguments.Count; int[] parametersPositions = null; int? unmatchedArgumentIndex = null; bool? unmatchedArgumentIsNamed = null; // Try to map every argument position to a formal parameter position: for (int argumentPosition = 0; argumentPosition < argumentCount; ++argumentPosition) { // We use -1 as a sentinel to mean that no parameter was found that corresponded to this argument. bool isNamedArgument; int parameterPosition = CorrespondsToAnyParameter(parameters, expanded, arguments, argumentPosition, out isNamedArgument) ?? -1; if (parameterPosition == -1 && unmatchedArgumentIndex == null) { unmatchedArgumentIndex = argumentPosition; unmatchedArgumentIsNamed = isNamedArgument; } if (parameterPosition != argumentPosition && parametersPositions == null) { parametersPositions = new int[argumentCount]; for (int i = 0; i < argumentPosition; ++i) { parametersPositions[i] = i; } } if (parametersPositions != null) { parametersPositions[argumentPosition] = parameterPosition; } } ParameterMap argsToParameters = new ParameterMap(parametersPositions, argumentCount); // We have analyzed every argument and tried to make it correspond to a particular parameter. // There are now three questions we must answer: // // (1) Is there any argument without a corresponding parameter? // (2) was there any named argument that specified a parameter that was already // supplied with a positional parameter? // (3) Is there any non-optional parameter without a corresponding argument? // // If the answer to any of these questions is "yes" then the method is not applicable. // It is possible that the answer to any number of these questions is "yes", and so // we must decide which error condition to prioritize when reporting the error, // should we need to report why a given method is not applicable. We prioritize // them in the given order. // (1) Is there any argument without a corresponding parameter? if (unmatchedArgumentIndex != null) { if (unmatchedArgumentIsNamed.Value) { return ArgumentAnalysisResult.NoCorrespondingNamedParameter(unmatchedArgumentIndex.Value); } else { return ArgumentAnalysisResult.NoCorrespondingParameter(unmatchedArgumentIndex.Value); } } // (2) was there any named argument that specified a parameter that was already // supplied with a positional parameter? int? nameUsedForPositional = NameUsedForPositional(arguments, argsToParameters); if (nameUsedForPositional != null) { return ArgumentAnalysisResult.NameUsedForPositional(nameUsedForPositional.Value); } // (3) Is there any non-optional parameter without a corresponding argument? int? requiredParameterMissing = CheckForMissingRequiredParameter(argsToParameters, parameters, isMethodGroupConversion, expanded); if (requiredParameterMissing != null) { return ArgumentAnalysisResult.RequiredParameterMissing(requiredParameterMissing.Value); } // __arglist cannot be used with named arguments (as it doesn't have a name) if (arguments.Names.Count != 0 && symbol.GetIsVararg()) { return ArgumentAnalysisResult.RequiredParameterMissing(parameters.Length); } // We're good; this one might be applicable in the given form. return expanded ? ArgumentAnalysisResult.ExpandedForm(argsToParameters.ToImmutableArray()) : ArgumentAnalysisResult.NormalForm(argsToParameters.ToImmutableArray()); }
private static int? CorrespondsToAnyParameter( ImmutableArray<ParameterSymbol> memberParameters, bool expanded, AnalyzedArguments arguments, int argumentPosition, out bool isNamedArgument) { // Spec 7.5.1.1: Corresponding parameters: // For each argument in an argument list there has to be a corresponding parameter in // the function member or delegate being invoked. The parameter list used in the // following is determined as follows: // - For virtual methods and indexers defined in classes, the parameter list is picked from the most specific // declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes. // - For interface methods and indexers, the parameter list is picked form the most specific definition of the member, // starting with the interface type and searching through the base interfaces. If no unique parameter list is found, // a parameter list with inaccessible names and no optional parameters is constructed, so that invocations cannot use // named parameters or omit optional arguments. // - For partial methods, the parameter list of the defining partial method declaration is used. // - For all other other function members and delegates there is only a single parameter list, which is the one used. // // The position of an argument or parameter is defined as the number of arguments or // parameters preceding it in the argument list or parameter list. // // The corresponding parameters for function member arguments are established as follows: // // Arguments in the argument-list of instance constructors, methods, indexers and delegates: isNamedArgument = arguments.Names.Count > argumentPosition && arguments.Names[argumentPosition] != null; if (!isNamedArgument) { // Spec: // - A positional argument where a fixed parameter occurs at the same position in the // parameter list corresponds to that parameter. // - A positional argument of a function member with a parameter array invoked in its // normal form corresponds to the parameter array, which must occur at the same // position in the parameter list. // - A positional argument of a function member with a parameter array invoked in its // expanded form, where no fixed parameter occurs at the same position in the // parameter list, corresponds to an element in the parameter array. if (argumentPosition >= memberParameters.Length) { return expanded ? memberParameters.Length - 1 : (int?)null; } return argumentPosition; } else { // SPEC: A named argument corresponds to the parameter of the same name in the parameter list. // SPEC VIOLATION: The intention of this line of the specification, when contrasted with // SPEC VIOLATION: the lines on positional arguments quoted above, was to disallow a named // SPEC VIOLATION: argument from corresponding to an element of a parameter array when // SPEC VIOLATION: the method was invoked in its expanded form. That is to say that in // SPEC VIOLATION: this case: M(params int[] x) ... M(x : 1234); the named argument // SPEC VIOLATION: corresponds to x in the normal form (and is then inapplicable), but // SPEC VIOLATION: the named argument does *not* correspond to a member of params array // SPEC VIOLATION: x in the expanded form. // SPEC VIOLATION: Sadly that is not what we implemented in C# 4, and not what we are // SPEC VIOLATION: implementing here. If you do that, we make x correspond to the // SPEC VIOLATION: parameter array and allow the candidate to be applicable in its // SPEC VIOLATION: expanded form. var name = arguments.Names[argumentPosition]; for (int p = 0; p < memberParameters.Length; ++p) { // TODO: p is initialized to zero; is it ok for a named argument to "correspond" to // _any_ parameter, or just the parameters past the point of positional arguments? if (memberParameters[p].Name == name.Identifier.ValueText) { return p; } } } return null; }
private static ArgumentAnalysisResult AnalyzeArgumentsForNormalFormNoNamedArguments( ImmutableArray<ParameterSymbol> parameters, AnalyzedArguments arguments, bool isMethodGroupConversion, bool isVararg) { Debug.Assert(!parameters.IsDefault); Debug.Assert(arguments != null); Debug.Assert(arguments.Names.Count == 0); // We simulate an additional non-optional parameter for a vararg method. int parameterCount = parameters.Length + (isVararg ? 1 : 0); int argumentCount = arguments.Arguments.Count; // If there are no named arguments then analyzing the argument and parameter // matching in normal form is simple: each argument corresponds exactly to // the matching parameter, and if there are not enough arguments then the // unmatched parameters had better all be optional. If there are too // few parameters then one of the arguments has no matching parameter. // Otherwise, everything is just right. if (argumentCount < parameterCount) { for (int parameterPosition = argumentCount; parameterPosition < parameterCount; ++parameterPosition) { if (parameters.Length == parameterPosition || !CanBeOptional(parameters[parameterPosition], isMethodGroupConversion)) { return ArgumentAnalysisResult.RequiredParameterMissing(parameterPosition); } } } else if (parameterCount < argumentCount) { return ArgumentAnalysisResult.NoCorrespondingParameter(parameterCount); } // A null map means that every argument in the argument list corresponds exactly to // the same position in the formal parameter list. return ArgumentAnalysisResult.NormalForm(default(ImmutableArray<int>)); }
private static bool ReportQueryInferenceFailedSelectMany(FromClauseSyntax fromClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray<Symbol> symbols, DiagnosticBag diagnostics) { Debug.Assert(methodName == "SelectMany"); // Estimate the return type of Select's lambda argument BoundExpression arg = arguments.Argument(arguments.IsExtensionMethodInvocation ? 1 : 0); TypeSymbol type = null; if (arg.Kind == BoundKind.UnboundLambda) { var unbound = (UnboundLambda)arg; foreach (var t in unbound.Data.InferredReturnTypes()) { if (!t.IsErrorType()) { type = t; break; } } } if ((object)type == null || type.IsErrorType()) { return false; } TypeSymbol receiverType = receiver?.Type; diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryTypeInferenceFailedSelectMany, new object[] { type, receiverType, methodName }, symbols), fromClause.Expression.Location); return true; }
internal static void ReportQueryInferenceFailed(CSharpSyntaxNode queryClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray <Symbol> symbols, DiagnosticBag diagnostics) { string clauseKind = null; bool multiple = false; switch (queryClause.Kind) { case SyntaxKind.JoinClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.JoinKeyword); multiple = true; break; case SyntaxKind.LetClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.LetKeyword); break; case SyntaxKind.SelectClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.SelectKeyword); break; case SyntaxKind.WhereClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.WhereKeyword); break; case SyntaxKind.OrderByClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: clauseKind = SyntaxFacts.GetText(SyntaxKind.OrderByKeyword); multiple = true; break; case SyntaxKind.QueryContinuation: clauseKind = SyntaxFacts.GetText(SyntaxKind.IntoKeyword); break; case SyntaxKind.GroupClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.GroupKeyword) + " " + SyntaxFacts.GetText(SyntaxKind.ByKeyword); multiple = true; break; case SyntaxKind.FromClause: if (ReportQueryInferenceFailedSelectMany((FromClauseSyntax)queryClause, methodName, receiver, arguments, symbols, diagnostics)) { return; } clauseKind = SyntaxFacts.GetText(SyntaxKind.FromKeyword); break; default: clauseKind = "unknown"; Debug.Assert(false, "invalid query clause kind " + queryClause.Kind); break; } diagnostics.Add(new DiagnosticInfoWithSymbols( multiple ? ErrorCode.ERR_QueryTypeInferenceFailedMulti : ErrorCode.ERR_QueryTypeInferenceFailed, new object[] { clauseKind, methodName }, symbols), queryClause.GetFirstToken().GetLocation()); }
internal static void ReportQueryInferenceFailed(CSharpSyntaxNode queryClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray<Symbol> symbols, DiagnosticBag diagnostics) { string clauseKind = null; bool multiple = false; switch (queryClause.Kind) { case SyntaxKind.JoinClause: clauseKind = "join"; // TODO: should be ErrorCode.IDS_JoinClause.Localize(); multiple = true; break; case SyntaxKind.LetClause: clauseKind = "let"; // TODO: ErrorCode.IDS_LetClause break; case SyntaxKind.SelectClause: clauseKind = "select"; // TODO ErrorCode.IDS_Select break; case SyntaxKind.WhereClause: clauseKind = "where"; // TODO: ErrorCode.IDS_Where break; case SyntaxKind.OrderByClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: clauseKind = "order by"; // TODO: ErrorCode.IDS_OrderByClause multiple = true; break; case SyntaxKind.QueryContinuation: clauseKind = "into"; // TODO: ErrorCode.IDS_ContinuationClause break; case SyntaxKind.GroupClause: clauseKind = "group by"; // TODO: ErrorCode.IDS_GroupByClause multiple = true; break; case SyntaxKind.FromClause: if (ReportQueryInferenceFailedSelectMany((FromClauseSyntax)queryClause, methodName, receiver, arguments, symbols, diagnostics)) { return; } clauseKind = "from"; // TODO: ErrorCode.IDS_FromClause break; default: clauseKind = "unknown"; Debug.Assert(false, "invalid query clause kind " + queryClause.Kind); break; } diagnostics.Add(new DiagnosticInfoWithSymbols( multiple ? ErrorCode.ERR_QueryTypeInferenceFailedMulti : ErrorCode.ERR_QueryTypeInferenceFailed, new object[] { clauseKind, methodName }, symbols), queryClause.GetFirstToken().GetLocation()); }
/// <summary> /// Resolve method group based on the optional delegate invoke method. /// If the invoke method is null, ignore arguments in resolution. /// </summary> private static MethodGroupResolution ResolveDelegateMethodGroup(Binder binder, BoundMethodGroup source, MethodSymbol delegateInvokeMethodOpt, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { if ((object)delegateInvokeMethodOpt != null) { var analyzedArguments = new AnalyzedArguments(); GetDelegateArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); var resolution = binder.ResolveMethodGroup(source, analyzedArguments, isMethodGroupConversion: true, inferWithDynamic: true, useSiteDiagnostics: ref useSiteDiagnostics); return resolution; } else { return binder.ResolveMethodGroup(source, null, isMethodGroupConversion: true, useSiteDiagnostics: ref useSiteDiagnostics); } }
internal static void ReportQueryInferenceFailed(CSharpSyntaxNode queryClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray<Symbol> symbols, DiagnosticBag diagnostics) { string clauseKind = null; bool multiple = false; switch (queryClause.Kind()) { case SyntaxKind.JoinClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.JoinKeyword); multiple = true; break; case SyntaxKind.LetClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.LetKeyword); break; case SyntaxKind.SelectClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.SelectKeyword); break; case SyntaxKind.WhereClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.WhereKeyword); break; case SyntaxKind.OrderByClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: clauseKind = SyntaxFacts.GetText(SyntaxKind.OrderByKeyword); multiple = true; break; case SyntaxKind.QueryContinuation: clauseKind = SyntaxFacts.GetText(SyntaxKind.IntoKeyword); break; case SyntaxKind.GroupClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.GroupKeyword) + " " + SyntaxFacts.GetText(SyntaxKind.ByKeyword); multiple = true; break; case SyntaxKind.FromClause: if (ReportQueryInferenceFailedSelectMany((FromClauseSyntax)queryClause, methodName, receiver, arguments, symbols, diagnostics)) { return; } clauseKind = SyntaxFacts.GetText(SyntaxKind.FromKeyword); break; default: clauseKind = "unknown"; Debug.Assert(false, "invalid query clause kind " + queryClause.Kind()); break; } diagnostics.Add(new DiagnosticInfoWithSymbols( multiple ? ErrorCode.ERR_QueryTypeInferenceFailedMulti : ErrorCode.ERR_QueryTypeInferenceFailed, new object[] { clauseKind, methodName }, symbols), queryClause.GetFirstToken().GetLocation()); }
public static void GetDelegateArguments(CSharpSyntaxNode syntax, AnalyzedArguments analyzedArguments, ImmutableArray<ParameterSymbol> delegateParameters, CSharpCompilation compilation) { foreach (var p in delegateParameters) { ParameterSymbol parameter = p; // In ExpressionBinder::BindGrpConversion, the native compiler substitutes object in place of dynamic. This is // necessary because conversions from expressions of type dynamic always succeed, whereas conversions from the // type generally fail (modulo identity conversions). This is not reflected in the C# 4 spec, but will be // incorporated going forward. See DevDiv #742345 for additional details. // NOTE: Dev11 does a deep substitution (e.g. C<C<C<dynamic>>> -> C<C<C<object>>>), but that seems redundant. if (parameter.Type.IsDynamic()) { // If we don't have System.Object, then we'll get an error type, which will cause overload resolution to fail, // which will cause some error to be reported. That's sufficient (i.e. no need to specifically report its absence here). parameter = new SignatureOnlyParameterSymbol( compilation.GetSpecialType(SpecialType.System_Object), parameter.CustomModifiers, parameter.IsParams, parameter.RefKind); } analyzedArguments.Arguments.Add(new BoundParameter(syntax, parameter) { WasCompilerGenerated = true }); analyzedArguments.RefKinds.Add(parameter.RefKind); } }
internal static void ReportQueryInferenceFailed(CSharpSyntaxNode queryClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray <Symbol> symbols, DiagnosticBag diagnostics) { string clauseKind = null; bool multiple = false; switch (queryClause.Kind) { case SyntaxKind.JoinClause: clauseKind = "join"; // TODO: should be ErrorCode.IDS_JoinClause.Localize(); multiple = true; break; case SyntaxKind.LetClause: clauseKind = "let"; // TODO: ErrorCode.IDS_LetClause break; case SyntaxKind.SelectClause: clauseKind = "select"; // TODO ErrorCode.IDS_Select break; case SyntaxKind.WhereClause: clauseKind = "where"; // TODO: ErrorCode.IDS_Where break; case SyntaxKind.OrderByClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: clauseKind = "order by"; // TODO: ErrorCode.IDS_OrderByClause multiple = true; break; case SyntaxKind.QueryContinuation: clauseKind = "into"; // TODO: ErrorCode.IDS_ContinuationClause break; case SyntaxKind.GroupClause: clauseKind = "group by"; // TODO: ErrorCode.IDS_GroupByClause multiple = true; break; case SyntaxKind.FromClause: if (ReportQueryInferenceFailedSelectMany((FromClauseSyntax)queryClause, methodName, receiver, arguments, symbols, diagnostics)) { return; } clauseKind = "from"; // TODO: ErrorCode.IDS_FromClause break; default: clauseKind = "unknown"; Debug.Assert(false, "invalid query clause kind " + queryClause.Kind); break; } diagnostics.Add(new DiagnosticInfoWithSymbols( multiple ? ErrorCode.ERR_QueryTypeInferenceFailedMulti : ErrorCode.ERR_QueryTypeInferenceFailed, new object[] { clauseKind, methodName }, symbols), queryClause.GetFirstToken().GetLocation()); }