Implement non-trailing named arguments in Microsoft.CSharp (dotnet/corefx#25819)
authorJon Hanna <jon@hackcraft.net>
Fri, 19 Jan 2018 01:48:39 +0000 (01:48 +0000)
committerVladimir Sadov <vsadov@microsoft.com>
Fri, 19 Jan 2018 01:48:39 +0000 (17:48 -0800)
* * Remove CMethodIterator._bcanIncludeExtensionsInResults

Always false.

* Remove CMethodIterator._allowBogusAndInaccessible

Always true.

* Remove MemberLookup._results

Never used.

* Remove paths for _pContainingTypes being empty.

Never happens.

* Remove _bAtEnd

Check on correct use unnecessary in internal class.

* Remove unused parameters

* Replace private field-only properties with field access.

* Getter methods to properties; auto when appropriate.

* Remove _bIsCheckingInstanceMethods

Always true.

* Readonly fields where possible.

* Rename Hungarian-style names and remove redundant initializers.

* GroupToArgsBinderResult members to auto properties.

* Remove InconvertibleResult/_inconvertibleResults

Set but never accessed.

* Match error code numbers to corresponding C# errors.

Not clear why these were ever different, but matching them makes the
connection clearer.

* Add some initial (not yet passing) tests.

* Allow non-trailing arguments

* Detect misnamed arguments in iterator.

* Exclude methods from consideration if non-trailing names don't match.

* Correct error for name not found in non-trailing.

* Catch CS8328 errors.

* Return temporarily removed assertion.

* Correctly handle correctly naming the first of several params arguments

* Add tests based on Roslyn's tests for non-trailing named arguments.

* Fix handling when name is changed in an override.

Use the name defined on the type of the argument if UseCompileTimeType
or the type of the object, otherwise.

* Respond to PR feedback

Commit migrated from https://github.com/dotnet/corefx/commit/62ba485189b871a956288c8a36cf2c0647511e58

13 files changed:
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorCode.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFacts.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs
src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs
src/libraries/Microsoft.CSharp/src/Resources/Strings.resx
src/libraries/Microsoft.CSharp/tests/BindingErrors.cs
src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj
src/libraries/Microsoft.CSharp/tests/NamedArgumentTests.cs [new file with mode: 0644]

index 58da5c7..c762ace 100644 (file)
@@ -63,11 +63,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors
         ERR_AssgReadonly2 = 1648,
         ERR_AssgReadonlyStatic2 = 1650,
         ERR_BadCtorArgCount = 1729,
+        ERR_BadNamedArgument = 1739,
+        ERR_DuplicateNamedArgument = 1740,
+        ERR_NamedArgumentUsedInPositional = 1744,
+        ERR_BadNamedArgumentForDelegateInvoke = 1746,
         ERR_NonInvocableMemberCalled = 1955,
-        ERR_NamedArgumentSpecificationBeforeFixedArgument = 5002,
-        ERR_BadNamedArgument = 5003,
-        ERR_BadNamedArgumentForDelegateInvoke = 5004,
-        ERR_DuplicateNamedArgument = 5005,
-        ERR_NamedArgumentUsedInPositional = 5006,
+        ERR_BadNonTrailingNamedArgument = 8323
     }
 }
index 996c86c..81c4f73 100644 (file)
@@ -188,9 +188,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors
                 case ErrorCode.ERR_NonInvocableMemberCalled:
                     codeStr = SR.NonInvocableMemberCalled;
                     break;
-                case ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument:
-                    codeStr = SR.NamedArgumentSpecificationBeforeFixedArgument;
-                    break;
                 case ErrorCode.ERR_BadNamedArgument:
                     codeStr = SR.BadNamedArgument;
                     break;
@@ -203,6 +200,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors
                 case ErrorCode.ERR_NamedArgumentUsedInPositional:
                     codeStr = SR.NamedArgumentUsedInPositional;
                     break;
+                case ErrorCode.ERR_BadNonTrailingNamedArgument:
+                    codeStr = SR.BadNonTrailingNamedArgument;
+                    break;
+
                 default:
                     // means missing resources match the code entry
                     Debug.Assert(false, "Missing resources for the error " + code.ToString());
index 57e14f4..6fe31c2 100644 (file)
@@ -137,6 +137,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 Debug.Assert(!(args.prgexpr[i] is ExprNamedArgumentSpecification));
             }
 #endif
+            // If we've no args we can skip. If the last argument isn't named then either we
+            // have no named arguments, and we can skip, or we have non-trailing named arguments
+            // and we MUST skip!
+            if (args.carg == 0 || !(args.prgexpr[args.carg - 1] is ExprNamedArgumentSpecification))
+            {
+                return pta;
+            }
 
             CType type = pTypeThrough != null ? pTypeThrough : mpwi.GetType();
             CType[] typeList = new CType[pta.Count];
index a7024cf..00ca1e2 100644 (file)
@@ -820,7 +820,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
         ////////////////////////////////////////////////////////////////////////////////
         // Given a method group or indexer group, bind it to the arguments for an 
         // invocation.
-        private GroupToArgsBinderResult BindMethodGroupToArgumentsCore(BindingFlag bindFlags, ExprMemberGroup grp, Expr args, int carg, bool bHasNamedArgumentSpecifiers)
+        private GroupToArgsBinderResult BindMethodGroupToArgumentsCore(BindingFlag bindFlags, ExprMemberGroup grp, Expr args, int carg, NamedArgumentsKind namedArgumentsKind)
         {
             ArgInfos pargInfo = new ArgInfos {carg = carg};
             FillInArgInfoFromArgList(pargInfo, args);
@@ -828,7 +828,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
             ArgInfos pOriginalArgInfo = new ArgInfos {carg = carg};
             FillInArgInfoFromArgList(pOriginalArgInfo, args);
 
-            GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, pargInfo, pOriginalArgInfo, bHasNamedArgumentSpecifiers);
+            GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, pargInfo, pOriginalArgInfo, namedArgumentsKind);
             binder.Bind();
 
             return binder.GetResultsOfBind();
@@ -846,17 +846,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
 
             Debug.Assert(grp.Name != null);
 
-            // If we have named arguments specified, make sure we have them all appearing after
-            // fixed arguments.
-            bool seenNamed = VerifyNamedArgumentsAfterFixed(args);
+            // Do we have named arguments specified, are they after fixed arguments (can position) or
+            // non-trailing (can rule out methods only).
+            NamedArgumentsKind namedKind = FindNamedArgumentsType(args);
 
-            MethPropWithInst mpwiBest = BindMethodGroupToArgumentsCore(bindFlags, grp, args, carg, seenNamed)
-                .GetBestResult();
+            MethPropWithInst mpwiBest = BindMethodGroupToArgumentsCore(bindFlags, grp, args, carg, namedKind).BestResult;
             if (grp.SymKind == SYMKIND.SK_PropertySymbol)
             {
                 Debug.Assert((grp.Flags & EXPRFLAG.EXF_INDEXER) != 0);
-                //PropWithType pwt = new PropWithType(mpwiBest.Prop(), mpwiBest.GetType());
-
                 return BindToProperty(grp.OptionalObject, new PropWithType(mpwiBest), bindFlags, args, grp);
             }
 
@@ -865,10 +862,16 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
 
         /////////////////////////////////////////////////////////////////////////////////
 
-        private bool VerifyNamedArgumentsAfterFixed(Expr args)
+        public enum NamedArgumentsKind
+        {
+            None,
+            Positioning,
+            NonTrailing
+        }
+
+        private static NamedArgumentsKind FindNamedArgumentsType(Expr args)
         {
             Expr list = args;
-            bool seenNamed = false;
             while (list != null)
             {
                 Expr arg;
@@ -886,18 +889,30 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 Debug.Assert(arg != null);
                 if (arg is ExprNamedArgumentSpecification)
                 {
-                    seenNamed = true;
-                }
-                else
-                {
-                    if (seenNamed)
+                    while (list != null)
                     {
-                        throw GetErrorContext().Error(ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument);
+                        if (list is ExprList nextList)
+                        {
+                            arg = nextList.OptionalElement;
+                            list = nextList.OptionalNextListNode;
+                        }
+                        else
+                        {
+                            arg = list;
+                            list = null;
+                        }
+
+                        if (!(arg is ExprNamedArgumentSpecification))
+                        {
+                            return NamedArgumentsKind.NonTrailing;
+                        }
                     }
+
+                    return NamedArgumentsKind.Positioning;
                 }
             }
 
-            return seenNamed;
+            return NamedArgumentsKind.None;
         }
 
         ////////////////////////////////////////////////////////////////////////////////
@@ -1277,6 +1292,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                             }
                             index++;
                         }
+
                         Debug.Assert(index != mp.Params.Count);
                         CType substDestType = GetTypes().SubstType(@params[index], type, pTypeArgs);
 
index 7b172f6..ca14129 100644 (file)
@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Linq;
 using Microsoft.CSharp.RuntimeBinder.Errors;
 using Microsoft.CSharp.RuntimeBinder.Syntax;
 
@@ -32,7 +33,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
             private readonly ExprMemberGroup _pGroup;
             private readonly ArgInfos _pArguments;
             private readonly ArgInfos _pOriginalArguments;
-            private readonly bool _bHasNamedArguments;
+            private readonly NamedArgumentsKind _namedArgumentsKind;
             private AggregateType _pCurrentType;
             private MethodOrPropertySymbol _pCurrentSym;
             private TypeArray _pCurrentTypeArgs;
@@ -43,6 +44,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
             private readonly List<CandidateFunctionMember> _methList;
             private readonly MethPropWithInst _mpwiParamTypeConstraints;
             private readonly MethPropWithInst _mpwiBogus;
+            private readonly MethPropWithInst _misnamed;
             private readonly MethPropWithInst _mpwiCantInferInstArg;
             private readonly MethWithType _mwtBadArity;
             private Name _pInvalidSpecifiedName;
@@ -55,7 +57,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
             private readonly List<CType> _HiddenTypes;
             private bool _bArgumentsChangedForNamedOrOptionalArguments;
 
-            public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, ExprMemberGroup grp, ArgInfos args, ArgInfos originalArgs, bool bHasNamedArguments)
+            public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, ExprMemberGroup grp, ArgInfos args, ArgInfos originalArgs, NamedArgumentsKind namedArgumentsKind)
             {
                 Debug.Assert(grp != null);
                 Debug.Assert(exprBinder != null);
@@ -67,7 +69,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 _pGroup = grp;
                 _pArguments = args;
                 _pOriginalArguments = originalArgs;
-                _bHasNamedArguments = bHasNamedArguments;
+                _namedArgumentsKind = namedArgumentsKind;
                 _pCurrentType = null;
                 _pCurrentSym = null;
                 _pCurrentTypeArgs = null;
@@ -77,6 +79,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 _methList = new List<CandidateFunctionMember>();
                 _mpwiParamTypeConstraints = new MethPropWithInst();
                 _mpwiBogus = new MethPropWithInst();
+                _misnamed = new MethPropWithInst();
                 _mpwiCantInferInstArg = new MethPropWithInst();
                 _mwtBadArity = new MethWithType();
                 _HiddenTypes = new List<CType>();
@@ -134,9 +137,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 // iterator will only return propsyms (or methsyms, or whatever)
                 symbmask_t mask = (symbmask_t)(1 << (int)_pGroup.SymKind);
 
-                CType pTypeThrough = _pGroup.OptionalObject?.Type;
-                CMemberLookupResults.CMethodIterator iterator = _pGroup.MemberLookupResults.GetMethodIterator(GetSemanticChecker(), GetSymbolLoader(), pTypeThrough, GetTypeQualifier(_pGroup), _pExprBinder.ContextForMemberLookup(), true, // AllowBogusAndInaccessible
-                    false, _pGroup.TypeArgs.Count, _pGroup.Flags, mask);
+                CMemberLookupResults.CMethodIterator iterator = _pGroup.MemberLookupResults.GetMethodIterator(GetSemanticChecker(), GetSymbolLoader(), GetTypeQualifier(_pGroup), _pExprBinder.ContextForMemberLookup(), _pGroup.TypeArgs.Count, _pGroup.Flags, mask, _namedArgumentsKind == NamedArgumentsKind.NonTrailing ? _pOriginalArguments : null);
                 while (true)
                 {
                     bool bFoundExpanded;
@@ -172,7 +173,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                     // If we have named arguments, reorder them for this method.
                     // If we don't have Exprs, its because we're doing a method group conversion.
                     // In those scenarios, we never want to add named arguments or optional arguments.
-                    if (_bHasNamedArguments)
+                    if (_namedArgumentsKind == NamedArgumentsKind.Positioning)
                     {
                         if (!ReOrderArgsForNamedArguments())
                         {
@@ -203,7 +204,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
 
                     // If we cant use the current symbol, then we've filtered it, so get the next one.
 
-                    if (!iterator.CanUseCurrentSymbol())
+                    if (!iterator.CanUseCurrentSymbol)
                     {
                         continue;
                     }
@@ -217,17 +218,25 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                     }
 
                     // Check access.
-                    bool fCanAccess = !iterator.IsCurrentSymbolInaccessible();
-                    if (!fCanAccess && (!_methList.IsEmpty() || _results.GetInaccessibleResult()))
+                    bool fCanAccess = !iterator.IsCurrentSymbolInaccessible;
+                    if (!fCanAccess && (!_methList.IsEmpty() || _results.InaccessibleResult))
                     {
                         // We'll never use this one for error reporting anyway, so just skip it.
                         bSearchForExpanded = false;
                         continue;
                     }
 
+                    // Check misnamed.
+                    bool misnamed = fCanAccess && iterator.IsCurrentSymbolMisnamed;
+                    if (misnamed && (!_methList.IsEmpty() || _results.InaccessibleResult || _misnamed))
+                    {
+                        bSearchForExpanded = false;
+                        continue;
+                    }
+
                     // Check bogus.
-                    bool fBogus = fCanAccess && iterator.IsCurrentSymbolBogus();
-                    if (fBogus && (!_methList.IsEmpty() || _results.GetInaccessibleResult() || _mpwiBogus))
+                    bool fBogus = fCanAccess && !misnamed && iterator.IsCurrentSymbolBogus;
+                    if (fBogus && (!_methList.IsEmpty() || _results.InaccessibleResult || _mpwiBogus || _misnamed))
                     {
                         // We'll never use this one for error reporting anyway, so just skip it.
                         bSearchForExpanded = false;
@@ -246,8 +255,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                     {
                         // In case we never get an accessible method, this will allow us to give
                         // a better error...
-                        Debug.Assert(!_results.GetInaccessibleResult());
-                        _results.GetInaccessibleResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
+                        Debug.Assert(!_results.InaccessibleResult);
+                        _results.InaccessibleResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
+                    }
+                    else if (misnamed)
+                    {
+                        Debug.Assert(!_misnamed);
+                        _misnamed.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
                     }
                     else if (fBogus)
                     {
@@ -824,12 +838,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
             // return false.
             private bool GetNextSym(CMemberLookupResults.CMethodIterator iterator)
             {
-                if (!iterator.MoveNext(_methList.IsEmpty()))
+                if (!iterator.MoveNext())
                 {
                     return false;
                 }
-                _pCurrentSym = iterator.GetCurrentSymbol();
-                AggregateType type = iterator.GetCurrentType();
+                _pCurrentSym = iterator.CurrentSymbol;
+                AggregateType type = iterator.CurrentType;
 
                 // If our current type is null, this is our first iteration, so set the type.
                 // If our current type is not null, and we've got a new type now, and we've already matched
@@ -850,11 +864,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 while (_HiddenTypes.Contains(_pCurrentType))
                 {
                     // Move through this type and get the next one.
-                    for (; iterator.GetCurrentType() == _pCurrentType; iterator.MoveNext(_methList.IsEmpty())) ;
-                    _pCurrentSym = iterator.GetCurrentSymbol();
-                    _pCurrentType = iterator.GetCurrentType();
+                    for (; iterator.CurrentType == _pCurrentType; iterator.MoveNext()) ;
+                    _pCurrentSym = iterator.CurrentSymbol;
+                    _pCurrentType = iterator.CurrentType;
 
-                    if (iterator.AtEnd())
+                    if (iterator.AtEnd)
                     {
                         return false;
                     }
@@ -992,9 +1006,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                                 _nArgBest = ivar;
 
                                 // If we already have best method for instance methods don't overwrite with extensions
-                                if (!_results.GetBestResult())
+                                if (!_results.BestResult)
                                 {
-                                    _results.GetBestResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
+                                    _results.BestResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
                                 }
                             }
                             else if (ivar == _nArgBest && _pArguments.types[ivar] != var)
@@ -1009,20 +1023,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                                 if (argStripped == varStripped)
                                 {
                                     // If we already have best method for instance methods don't overwrite with extensions
-                                    if (!_results.GetBestResult())
+                                    if (!_results.BestResult)
                                     {
-                                        _results.GetBestResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
+                                        _results.BestResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs);
                                     }
                                 }
                             }
 
-                            if (_pCurrentSym is MethodSymbol meth)
-                            {
-                                // Do not store the result if we have an extension method and the instance 
-                                // parameter isn't convertible.
-
-                                _results.AddInconvertibleResult(meth, _pCurrentType, _pCurrentTypeArgs);
-                            }
                             return false;
                         }
                     }
@@ -1033,20 +1040,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                     if (_results.IsBetterUninferableResult(_pCurrentTypeArgs) && _pCurrentSym is MethodSymbol meth)
                     {
                         // If we're an instance method then mark us down.
-                        _results.GetUninferableResult().Set(meth, _pCurrentType, _pCurrentTypeArgs);
+                        _results.UninferableResult.Set(meth, _pCurrentType, _pCurrentTypeArgs);
                     }
-                }
-                else
-                {
-                    if (_pCurrentSym is MethodSymbol meth)
-                    {
-                        // Do not store the result if we have an extension method and the instance 
-                        // parameter isn't convertible.
 
-                        _results.AddInconvertibleResult(meth, _pCurrentType, _pCurrentTypeArgs);
-                    }
+                    return false;
                 }
-                return !containsErrorSym;
+
+                return true;
             }
 
             private void UpdateArguments()
@@ -1132,16 +1132,16 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 // used for Methods and Indexers
                 Debug.Assert(_pGroup.SymKind == SYMKIND.SK_MethodSymbol || _pGroup.SymKind == SYMKIND.SK_PropertySymbol && 0 != (_pGroup.Flags & EXPRFLAG.EXF_INDEXER));
                 Debug.Assert(_pGroup.TypeArgs.Count == 0 || _pGroup.SymKind == SYMKIND.SK_MethodSymbol);
-                Debug.Assert(0 == (_pGroup.Flags & EXPRFLAG.EXF_USERCALLABLE) || _results.GetBestResult().MethProp().isUserCallable());
+                Debug.Assert(0 == (_pGroup.Flags & EXPRFLAG.EXF_USERCALLABLE) || _results.BestResult.MethProp().isUserCallable());
 
                 if (_pGroup.SymKind == SYMKIND.SK_MethodSymbol)
                 {
-                    Debug.Assert(_results.GetBestResult().MethProp() is MethodSymbol);
+                    Debug.Assert(_results.BestResult.MethProp() is MethodSymbol);
 
-                    if (_results.GetBestResult().TypeArgs.Count > 0)
+                    if (_results.BestResult.TypeArgs.Count > 0)
                     {
                         // Check method type variable constraints.
-                        TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(_results.GetBestResult()));
+                        TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(_results.BestResult));
                     }
                 }
             }
@@ -1156,14 +1156,45 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
 
                 Debug.Assert(_methList.IsEmpty());
                 // Report inaccessible.
-                if (_results.GetInaccessibleResult())
+                if (_results.InaccessibleResult)
                 {
                     // We might have called this, but it is inaccessible...
-                    return GetSemanticChecker().ReportAccessError(_results.GetInaccessibleResult(), _pExprBinder.ContextForMemberLookup(), GetTypeQualifier(_pGroup));
+                    return GetSemanticChecker().ReportAccessError(_results.InaccessibleResult, _pExprBinder.ContextForMemberLookup(), GetTypeQualifier(_pGroup));
                 }
 
-                // Report bogus.
-                if (_mpwiBogus)
+                if (_misnamed)
+                {
+                    // Get exception immediately for the non-trailing named argument being misplaced.
+                    // Handle below for the name not being present at all.
+                    List<Name> paramNames = FindMostDerivedMethod(_misnamed.MethProp(), _pGroup.OptionalObject).ParameterNames;
+                    for (int i = 0; ; ++i)
+                    {
+                        if (i == _pOriginalArguments.carg)
+                        {
+                            // If we're here we had the correct name used for the first params argument.
+                            // Report it as not matching the correct number of arguments below.
+                            break;
+                        }
+
+                        if (_pOriginalArguments.prgexpr[i] is ExprNamedArgumentSpecification named)
+                        {
+                            Name name = named.Name;
+                            if (paramNames[i] != name)
+                            {
+                                // We have the bad name. Is it misplaced or absent?
+                                if (paramNames.Contains(name))
+                                {
+                                    return GetErrorContext().Error(ErrorCode.ERR_BadNonTrailingNamedArgument, name);
+                                }
+
+                                // Let this be handled by _pInvalidSpecifiedName handling.
+                                _pInvalidSpecifiedName = name;
+                                break;
+                            }
+                        }
+                    }
+                }
+                else if (_mpwiBogus)
                 {
                     // We might have called this, but it is bogus...
                     return GetErrorContext().Error(ErrorCode.ERR_BindToBogus, _mpwiBogus);
@@ -1178,29 +1209,29 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                         _pGroup.OptionalObject.Type.isDelegateType() &&
                         _pGroup.Name == NameManager.GetPredefinedName(PredefinedName.PN_INVOKE))
                 {
-                    Debug.Assert(!_results.GetBestResult() || _results.GetBestResult().MethProp().getClass().IsDelegate());
-                    Debug.Assert(!_results.GetBestResult() || _results.GetBestResult().GetType().getAggregate().IsDelegate());
+                    Debug.Assert(!_results.BestResult || _results.BestResult.MethProp().getClass().IsDelegate());
+                    Debug.Assert(!_results.BestResult || _results.BestResult.GetType().getAggregate().IsDelegate());
                     bUseDelegateErrors = true;
                     nameErr = _pGroup.OptionalObject.Type.getAggregate().name;
                 }
 
-                if (_results.GetBestResult())
+                if (_results.BestResult)
                 {
                     // If we had some invalid arguments for best matching.
                     return ReportErrorsForBestMatching(bUseDelegateErrors);
                 }
 
-                if (_results.GetUninferableResult() || _mpwiCantInferInstArg)
+                if (_results.UninferableResult || _mpwiCantInferInstArg)
                 {
-                    if (!_results.GetUninferableResult())
+                    if (!_results.UninferableResult)
                     {
                         //copy the extension method for which instance argument type inference failed
-                        _results.GetUninferableResult().Set(_mpwiCantInferInstArg.Sym as MethodSymbol, _mpwiCantInferInstArg.GetType(), _mpwiCantInferInstArg.TypeArgs);
+                        _results.UninferableResult.Set(_mpwiCantInferInstArg.Sym as MethodSymbol, _mpwiCantInferInstArg.GetType(), _mpwiCantInferInstArg.TypeArgs);
                     }
-                    Debug.Assert(_results.GetUninferableResult().Sym is MethodSymbol);
+                    Debug.Assert(_results.UninferableResult.Sym is MethodSymbol);
 
                     MethWithType mwtCantInfer = new MethWithType();
-                    mwtCantInfer.Set(_results.GetUninferableResult().Meth(), _results.GetUninferableResult().GetType());
+                    mwtCantInfer.Set(_results.UninferableResult.Meth(), _results.UninferableResult.GetType());
                     return GetErrorContext().Error(ErrorCode.ERR_CantInferMethTypeArgs, mwtCantInfer);
                 }
 
@@ -1261,10 +1292,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 if (bUseDelegateErrors)
                 {
                     // Point to the Delegate, not the Invoke method
-                    return GetErrorContext().Error(ErrorCode.ERR_BadDelArgTypes, _results.GetBestResult().GetType());
+                    return GetErrorContext().Error(ErrorCode.ERR_BadDelArgTypes, _results.BestResult.GetType());
                 }
 
-                return GetErrorContext().Error(ErrorCode.ERR_BadArgTypes, _results.GetBestResult());
+                return GetErrorContext().Error(ErrorCode.ERR_BadArgTypes, _results.BestResult);
             }
         }
     }
index 375741c..d6574fb 100644 (file)
@@ -15,44 +15,22 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
 
         internal sealed class GroupToArgsBinderResult
         {
-            public MethPropWithInst BestResult;
-            public MethPropWithInst GetBestResult() { return BestResult; }
-            public MethPropWithInst AmbiguousResult;
-            public MethPropWithInst GetAmbiguousResult() { return AmbiguousResult; }
-            private MethPropWithInst InaccessibleResult;
-            public MethPropWithInst GetInaccessibleResult() { return InaccessibleResult; }
-            private MethPropWithInst UninferableResult;
-            public MethPropWithInst GetUninferableResult() { return UninferableResult; }
-            private MethPropWithInst InconvertibleResult;
+            public MethPropWithInst BestResult { get; set; }
+
+            public MethPropWithInst AmbiguousResult { get; set; }
+
+            public MethPropWithInst InaccessibleResult { get; }
+
+            public MethPropWithInst UninferableResult { get; }
+
             public GroupToArgsBinderResult()
             {
                 BestResult = new MethPropWithInst();
                 AmbiguousResult = new MethPropWithInst();
                 InaccessibleResult = new MethPropWithInst();
                 UninferableResult = new MethPropWithInst();
-                InconvertibleResult = new MethPropWithInst();
-                _inconvertibleResults = new List<MethPropWithInst>();
-            }
-
-            private readonly List<MethPropWithInst> _inconvertibleResults;
-
-            /////////////////////////////////////////////////////////////////////////////////
-
-            public void AddInconvertibleResult(
-                MethodSymbol method,
-                AggregateType currentType,
-                TypeArray currentTypeArgs)
-            {
-                if (InconvertibleResult.Sym == null)
-                {
-                    // This is the first one, so set it for error reporting usage.
-                    InconvertibleResult.Set(method, currentType, currentTypeArgs);
-                }
-                _inconvertibleResults.Add(new MethPropWithInst(method, currentType, currentTypeArgs));
             }
 
-            /////////////////////////////////////////////////////////////////////////////////
-
             private static int NumberOfErrorTypes(TypeArray pTypeArgs)
             {
                 int nCount = 0;
index 8af0a44..748580e 100644 (file)
@@ -47,7 +47,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
         private Name _name;
         private int _arity;
         private MemLookFlags _flags;
-        private CMemberLookupResults _results;
 
         // For maintaining the type array. We throw the first 8 or so here.
         private readonly List<AggregateType> _rgtypeStart;
@@ -603,17 +602,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 }
             }
 
-            // if we are requested with extension methods
-            _results = new CMemberLookupResults(GetAllTypes(), _name);
-
             return !FError();
         }
 
-        public CMemberLookupResults GetResults()
-        {
-            return _results;
-        }
-
         // Whether there were errors.
         private bool FError()
         {
index 37f2649..045a6cd 100644 (file)
@@ -22,19 +22,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
                 TypeArray containingTypes,
                 Name name)
         {
+            Debug.Assert(containingTypes != null);
+            Debug.Assert(containingTypes.Count != 0);
             _pName = name;
             ContainingTypes = containingTypes;
-            if (ContainingTypes == null)
-            {
-                ContainingTypes = BSYMMGR.EmptyTypeArray();
-            }
         }
 
         public CMethodIterator GetMethodIterator(
-            CSemanticChecker pChecker, SymbolLoader pSymLoader, CType pObject, CType pQualifyingType, AggregateDeclaration pContext, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask)
+            CSemanticChecker pChecker, SymbolLoader pSymLoader, CType pQualifyingType, AggregateDeclaration pContext, int arity, EXPRFLAG flags, symbmask_t mask, ArgInfos nonTrailingNamedArguments)
         {
             Debug.Assert(pSymLoader != null);
-            CMethodIterator iterator = new CMethodIterator(pChecker, pSymLoader, _pName, ContainingTypes, pObject, pQualifyingType, pContext, allowBogusAndInaccessible, allowExtensionMethods, arity, flags, mask);
+            CMethodIterator iterator = new CMethodIterator(pChecker, pSymLoader, _pName, ContainingTypes, pQualifyingType, pContext, arity, flags, mask, nonTrailingNamedArguments);
             return iterator;
         }
 
index 6ab2a7d..4e86ea7 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Collections.Generic;
 using System.Diagnostics;
 using Microsoft.CSharp.RuntimeBinder.Syntax;
 
@@ -11,256 +12,158 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics
     {
         public partial class CMethodIterator
         {
-            private SymbolLoader _pSymbolLoader;
-            private CSemanticChecker _pSemanticChecker;
+            private readonly SymbolLoader _symbolLoader;
+            private readonly CSemanticChecker _semanticChecker;
             // Inputs.
-            private AggregateType _pCurrentType;
-            private MethodOrPropertySymbol _pCurrentSym;
-            private AggregateDeclaration _pContext;
-            private TypeArray _pContainingTypes;
-            private CType _pQualifyingType;
-            private Name _pName;
-            private int _nArity;
-            private symbmask_t _mask;
-            private EXPRFLAG _flags;
+            private readonly AggregateDeclaration _context;
+            private readonly TypeArray _containingTypes;
+            private readonly CType _qualifyingType;
+            private readonly Name _name;
+            private readonly int _arity;
+            private readonly symbmask_t _mask;
+            private readonly EXPRFLAG _flags;
+            private readonly ArgInfos _nonTrailingNamedArguments;
             // Internal state.
-            private int _nCurrentTypeCount;
-            private bool _bIsCheckingInstanceMethods;
-            private bool _bAtEnd;
-            private bool _bAllowBogusAndInaccessible;
-            // Flags for the current sym.
-            private bool _bCurrentSymIsBogus;
-            private bool _bCurrentSymIsInaccessible;
-            // if Extension can be part of the results that are returned by the iterator
-            // this may be false if an applicable instance method was found by bindgrptoArgs
-            private bool _bcanIncludeExtensionsInResults;
+            private int _currentTypeIndex;
 
-            public CMethodIterator(CSemanticChecker checker, SymbolLoader symLoader, Name name, TypeArray containingTypes, CType @object, CType qualifyingType, AggregateDeclaration context, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask)
+            public CMethodIterator(CSemanticChecker checker, SymbolLoader symLoader, Name name, TypeArray containingTypes, CType qualifyingType, AggregateDeclaration context, int arity, EXPRFLAG flags, symbmask_t mask, ArgInfos nonTrailingNamedArguments)
             {
                 Debug.Assert(name != null);
                 Debug.Assert(symLoader != null);
                 Debug.Assert(checker != null);
                 Debug.Assert(containingTypes != null);
-                _pSemanticChecker = checker;
-                _pSymbolLoader = symLoader;
-                _pCurrentType = null;
-                _pCurrentSym = null;
-                _pName = name;
-                _pContainingTypes = containingTypes;
-                _pQualifyingType = qualifyingType;
-                _pContext = context;
-                _bAllowBogusAndInaccessible = allowBogusAndInaccessible;
-                _nArity = arity;
+                Debug.Assert(containingTypes.Count != 0);
+                _semanticChecker = checker;
+                _symbolLoader = symLoader;
+                _name = name;
+                _containingTypes = containingTypes;
+                _qualifyingType = qualifyingType;
+                _context = context;
+                _arity = arity;
                 _flags = flags;
                 _mask = mask;
-                _nCurrentTypeCount = 0;
-                _bIsCheckingInstanceMethods = true;
-                _bAtEnd = false;
-                _bCurrentSymIsBogus = false;
-                _bCurrentSymIsInaccessible = false;
-                _bcanIncludeExtensionsInResults = allowExtensionMethods;
+                _nonTrailingNamedArguments = nonTrailingNamedArguments;
             }
-            public MethodOrPropertySymbol GetCurrentSymbol()
-            {
-                return _pCurrentSym;
-            }
-            public AggregateType GetCurrentType()
-            {
-                return _pCurrentType;
-            }
-            public bool IsCurrentSymbolInaccessible()
-            {
-                return _bCurrentSymIsInaccessible;
-            }
-            public bool IsCurrentSymbolBogus()
-            {
-                return _bCurrentSymIsBogus;
-            }
-            public bool MoveNext(bool canIncludeExtensionsInResults)
-            {
-                if (_bcanIncludeExtensionsInResults)
-                {
-                    _bcanIncludeExtensionsInResults = canIncludeExtensionsInResults;
-                }
 
-                if (_bAtEnd)
-                {
-                    return false;
-                }
+            public MethodOrPropertySymbol CurrentSymbol { get; private set; }
 
-                if (_pCurrentType == null) // First guy.
-                {
-                    if (_pContainingTypes.Count == 0)
-                    {
-                        // No instance methods, only extensions.
-                        _bIsCheckingInstanceMethods = false;
-                        _bAtEnd = true;
-                        return false;
-                    }
-                    else
-                    {
-                        if (!FindNextTypeForInstanceMethods())
-                        {
-                            // No instance or extensions.
+            public AggregateType CurrentType { get; private set; }
 
-                            _bAtEnd = true;
-                            return false;
-                        }
-                    }
-                }
-                if (!FindNextMethod())
-                {
-                    _bAtEnd = true;
-                    return false;
-                }
-                return true;
-            }
-            public bool AtEnd()
-            {
-                return _pCurrentSym == null;
-            }
-            private CSemanticChecker GetSemanticChecker()
-            {
-                return _pSemanticChecker;
-            }
-            private SymbolLoader GetSymbolLoader()
-            {
-                return _pSymbolLoader;
-            }
-            public bool CanUseCurrentSymbol()
-            {
-                _bCurrentSymIsInaccessible = false;
-                _bCurrentSymIsBogus = false;
+            public bool IsCurrentSymbolInaccessible { get; private set; }
 
-                // Make sure that whether we're seeing a ctor is consistent with the flag.
-                // The only properties we handle are indexers.
-                if (_mask == symbmask_t.MASK_MethodSymbol && (
-                        0 == (_flags & EXPRFLAG.EXF_CTOR) != !((MethodSymbol)_pCurrentSym).IsConstructor() ||
-                        0 == (_flags & EXPRFLAG.EXF_OPERATOR) != !((MethodSymbol)_pCurrentSym).isOperator) ||
-                    _mask == symbmask_t.MASK_PropertySymbol && !(_pCurrentSym is IndexerSymbol))
-                {
-                    // Get the next symbol.
-                    return false;
-                }
+            public bool IsCurrentSymbolBogus { get; private set; }
+
+            public bool IsCurrentSymbolMisnamed { get; private set; }
+
+            public bool MoveNext() => (CurrentType != null || FindNextTypeForInstanceMethods()) && FindNextMethod();
 
-                // If our arity is non-0, we must match arity with this symbol.
-                if (_nArity > 0)
+            public bool AtEnd => CurrentSymbol == null;
+
+            public bool CanUseCurrentSymbol
+            {
+                get
                 {
-                    if (_mask == symbmask_t.MASK_MethodSymbol && ((MethodSymbol)_pCurrentSym).typeVars.Count != _nArity)
+                    // Make sure that whether we're seeing a ctor is consistent with the flag.
+                    // The only properties we handle are indexers.
+                    if (_mask == symbmask_t.MASK_MethodSymbol && (
+                            0 == (_flags & EXPRFLAG.EXF_CTOR) != !((MethodSymbol)CurrentSymbol).IsConstructor() ||
+                            0 == (_flags & EXPRFLAG.EXF_OPERATOR) != !((MethodSymbol)CurrentSymbol).isOperator) ||
+                        _mask == symbmask_t.MASK_PropertySymbol && !(CurrentSymbol is IndexerSymbol))
                     {
+                        // Get the next symbol.
                         return false;
                     }
-                }
 
-                // If this guy's not callable, no good.
-                if (!ExpressionBinder.IsMethPropCallable(_pCurrentSym, (_flags & EXPRFLAG.EXF_USERCALLABLE) != 0))
-                {
-                    return false;
-                }
-
-                // Check access.
-                if (!GetSemanticChecker().CheckAccess(_pCurrentSym, _pCurrentType, _pContext, _pQualifyingType))
-                {
-                    // Sym is not accessible. However, if we're allowing inaccessible, then let it through and mark it.
-                    if (_bAllowBogusAndInaccessible)
+                    // If our arity is non-0, we must match arity with this symbol.
+                    if (_arity > 0 & _mask == symbmask_t.MASK_MethodSymbol && ((MethodSymbol)CurrentSymbol).typeVars.Count != _arity)
                     {
-                        _bCurrentSymIsInaccessible = true;
+                        return false;
                     }
-                    else
+
+                    // If this guy's not callable, no good.
+                    if (!ExpressionBinder.IsMethPropCallable(CurrentSymbol, (_flags & EXPRFLAG.EXF_USERCALLABLE) != 0))
                     {
                         return false;
                     }
+
+                    // Check access. If Sym is not accessible, then let it through and mark it.
+                    IsCurrentSymbolInaccessible = !_semanticChecker.CheckAccess(CurrentSymbol, CurrentType, _context, _qualifyingType);
+
+                    // Check bogus. If Sym is bogus, then let it through and mark it.
+                    IsCurrentSymbolBogus = CSemanticChecker.CheckBogus(CurrentSymbol);
+
+                    IsCurrentSymbolMisnamed = CheckArgumentNames();
+
+                    return true;
                 }
+            }
 
-                // Check bogus.
-                if (CSemanticChecker.CheckBogus(_pCurrentSym))
+            private bool CheckArgumentNames()
+            {
+                ArgInfos args = _nonTrailingNamedArguments;
+                if (args != null)
                 {
-                    // Sym is bogus, but if we're allow it, then let it through and mark it.
-                    if (_bAllowBogusAndInaccessible)
-                    {
-                        _bCurrentSymIsBogus = true;
-                    }
-                    else
+                    List<Name> paramNames = ExpressionBinder.GroupToArgsBinder
+                        .FindMostDerivedMethod(_symbolLoader, CurrentSymbol, _qualifyingType)
+                        .ParameterNames;
+
+                    List<Expr> argExpressions = args.prgexpr;
+                    for (int i = 0; i < args.carg; i++)
                     {
-                        return false;
+                        if (argExpressions[i] is ExprNamedArgumentSpecification named)
+                        {
+                            // Either wrong name, or correct name but we have more params arguments to follow.
+                            if (paramNames[i] != named.Name || i == paramNames.Count - 1 && i != args.carg - 1)
+                            {
+                                return true;
+                            }
+                        }
                     }
                 }
 
-                return _bIsCheckingInstanceMethods;
+                return false;
             }
 
             private bool FindNextMethod()
             {
-                while (true)
+                for (;;)
                 {
-                    if (_pCurrentSym == null)
-                    {
-                        _pCurrentSym = GetSymbolLoader().LookupAggMember(
-                                _pName, _pCurrentType.getAggregate(), _mask) as MethodOrPropertySymbol;
-                    }
-                    else
-                    {
-                        _pCurrentSym = SymbolLoader.LookupNextSym(
-                                _pCurrentSym, _pCurrentType.getAggregate(), _mask) as MethodOrPropertySymbol;
-                    }
+                    CurrentSymbol = (CurrentSymbol == null
+                        ? _symbolLoader.LookupAggMember(_name, CurrentType.getAggregate(), _mask)
+                        : SymbolLoader.LookupNextSym(CurrentSymbol, CurrentType.getAggregate(), _mask)) as MethodOrPropertySymbol;
 
                     // If we couldn't find a sym, we look up the type chain and get the next type.
-                    if (_pCurrentSym == null)
+                    if (CurrentSymbol == null)
                     {
-                        if (_bIsCheckingInstanceMethods)
+                        if (!FindNextTypeForInstanceMethods())
                         {
-                            if (!FindNextTypeForInstanceMethods() && _bcanIncludeExtensionsInResults)
-                            {
-                                // We didn't find any more instance methods, set us into extension mode.
-
-                                _bIsCheckingInstanceMethods = false;
-                            }
-                            else if (_pCurrentType == null && !_bcanIncludeExtensionsInResults)
-                            {
-                                return false;
-                            }
-                            else
-                            {
-                                // Found an instance method.
-                                continue;
-                            }
+                            return false;
                         }
-                        continue;
                     }
+                    else
+                    {
+                        // Note that we do not filter the current symbol for the user. They must do that themselves.
+                        // This is because for instance, BindGrpToArgs wants to filter on arguments before filtering
+                        // on bogosity.
 
-                    // Note that we do not filter the current symbol for the user. They must do that themselves.
-                    // This is because for instance, BindGrpToArgs wants to filter on arguments before filtering
-                    // on bogosity.
-
-                    // If we're here, we're good to go.
+                        // If we're here, we're good to go.
 
-                    break;
+                        return true;
+                    }
                 }
-                return true;
             }
 
             private bool FindNextTypeForInstanceMethods()
             {
-                // Otherwise, search through other types listed as well as our base class.
-                if (_pContainingTypes.Count > 0)
+                if (_currentTypeIndex >= _containingTypes.Count)
                 {
-                    if (_nCurrentTypeCount >= _pContainingTypes.Count)
-                    {
-                        // No more types to check.
-                        _pCurrentType = null;
-                    }
-                    else
-                    {
-                        _pCurrentType = _pContainingTypes[_nCurrentTypeCount++] as AggregateType;
-                    }
+                    // No more types to check.
+                    CurrentType = null;
+                    return false;
                 }
-                else
-                {
-                    // We have no more types to consider, so check out the base class.
 
-                    _pCurrentType = _pCurrentType.GetBaseClass();
-                }
-                return _pCurrentType != null;
+                CurrentType = _containingTypes[_currentTypeIndex++] as AggregateType;
+                return true;
             }
         }
     }
index 7b5bd31..462e5c1 100644 (file)
     <value>The operand of an increment or decrement operator must be a variable, property or indexer</value>
   </data>
   <data name="BadArgCount" xml:space="preserve">
-    <value>No overload for method '{0}' takes '{1}' arguments</value>
+    <value>No overload for method '{0}' takes {1} arguments</value>
   </data>
   <data name="BadArgTypes" xml:space="preserve">
     <value>The best overloaded method match for '{0}' has some invalid arguments</value>
   <data name="NonInvocableMemberCalled" xml:space="preserve">
     <value>Non-invocable member '{0}' cannot be used like a method.</value>
   </data>
-  <data name="NamedArgumentSpecificationBeforeFixedArgument" xml:space="preserve">
-    <value>Named argument specifications must appear after all fixed arguments have been specified</value>
-  </data>
   <data name="BadNamedArgument" xml:space="preserve">
     <value>The best overload for '{0}' does not have a parameter named '{1}'</value>
   </data>
   <data name="BindingNameCollision" xml:space="preserve">
     <value>More than one type in the binding has the same full name.</value>
   </data>
+  <data name="BadNonTrailingNamedArgument" xml:space="preserve">
+    <value>Named argument '{0}' is used out-of-position but is followed by an unnamed argument</value>
+  </data>
 </root>
index 77a7d62..2a0a3ab 100644 (file)
@@ -376,40 +376,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests
         }
 
         [Fact]
-        public void NamedArgumentBeforeFixedStatic()
-        {
-            CallSite<Func<CallSite, object, object, object, object>> site =
-                CallSite<Func<CallSite, object, object, object, object>>.Create(
-                    Binder.InvokeMember(
-                        CSharpBinderFlags.None, "Equals", null, GetType(),
-                        new[]
-                        {
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "objA"),
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
-                        }));
-            Func<CallSite, object, object, object, object> target = site.Target;
-            Assert.Throws<RuntimeBinderException>(() => target.Invoke(site, typeof(object), 2, 2));
-        }
-
-        [Fact]
-        public void NamedArgumentBeforeFixedInstance()
-        {
-            CallSite<Func<CallSite, object, object, object, object>> site =
-                CallSite<Func<CallSite, object, object, object, object>>.Create(
-                    Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
-                        CSharpBinderFlags.None, "Equals", null, GetType(),
-                        new[]
-                        {
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
-                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
-                        }));
-            Func<CallSite, object, object, object, object> target = site.Target;
-            Assert.Throws<RuntimeBinderException>(() => target.Invoke(site, EqualityComparer<int>.Default, 2, 2));
-        }
-
-        [Fact]
         public void DuplicateNamedArgument()
         {
             CallSite<Func<CallSite, object, object, object, object>> site =
index e8f1461..69ec051 100644 (file)
@@ -22,6 +22,7 @@
     <Compile Include="IntegerBinaryOperationTests.cs" />
     <Compile Include="IntegerUnaryOperationTests.cs" />
     <Compile Include="IsEventTests.cs" />
+    <Compile Include="NamedArgumentTests.cs" />
     <Compile Include="NullableEnumUnaryOperationTest.cs" />
     <Compile Include="RuntimeBinderExceptionTests.cs" />
     <Compile Include="RuntimeBinderInternalCompilerExceptionTests.cs" />
diff --git a/src/libraries/Microsoft.CSharp/tests/NamedArgumentTests.cs b/src/libraries/Microsoft.CSharp/tests/NamedArgumentTests.cs
new file mode 100644 (file)
index 0000000..989a50d
--- /dev/null
@@ -0,0 +1,1315 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+namespace Microsoft.CSharp.RuntimeBinder.Tests
+{
+    public class NamedArgumentTests
+    {
+        public class TypeWithMethods
+        {
+            public int DoStuff(int x, int y) => x + y;
+
+            public long DoStuff(long z, long a) => z * a;
+
+            public int DoStuff(string s, int i) => i;
+
+            public int DoOtherStuff(int x, int y, int z) => x * y + z;
+        }
+
+        [Fact]
+        public void OnlyNameFirstArgument()
+        {
+            CallSite<Func<CallSite, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object> target = callsite.Target;
+            object res = target(callsite, new TypeWithMethods(), 9, 14);
+            Assert.Equal(23, res);
+        }
+
+        [Fact]
+        public void OnlyNameFirstArgumentMatchesWrongType()
+        {
+            CallSite<Func<CallSite, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "s"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object> target = callsite.Target;
+            string message = Assert.Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14))
+                .Message;
+
+            // All but the (string, int) method should have been excluded from consideration, leaving the binder to
+            // complain about it not matching types.
+            Assert.Contains(
+                "'Microsoft.CSharp.RuntimeBinder.Tests.NamedArgumentTests.TypeWithMethods.DoStuff(string, int)'",
+                message);
+        }
+
+        [Fact]
+        public void ResolveThroughNameOnlyFirstArgument()
+        {
+            CallSite<Func<CallSite, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object> target = callsite.Target;
+            object res = target(callsite, new TypeWithMethods(), 9, 14);
+            Assert.Equal(126L, res);
+        }
+
+        [Fact]
+        public void NonExistentNameOnlyFirstArgument()
+        {
+            CallSite<Func<CallSite, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object> target = callsite.Target;
+            string message = Assert.Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14))
+                .Message;
+
+            //  The best overload for 'DoStuff' does not have a parameter named 'nada'
+            Assert.Contains("'DoStuff'", message);
+            Assert.Contains("'nada'", message);
+        }
+
+        [Fact]
+        public void NameOnlyFirstArgumentWrongPlace()
+        {
+            CallSite<Func<CallSite, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "y"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object> target = callsite.Target;
+            string message = Assert.Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14))
+                .Message;
+
+            //  Named argument 'y' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'y'", message);
+        }
+
+        [Fact]
+        public void NameOnlyFirstAndSecondWithSecondArgumentWrongPlace()
+        {
+            CallSite<Func<CallSite, object, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object, object> target = callsite.Target;
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14, 13))
+                .Message;
+
+            //  Named argument 'z' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'z'", message);
+        }
+
+        [Fact]
+        public void NameOnlyFirstAndSecondWithSecondArgumentNotFound()
+        {
+            CallSite<Func<CallSite, object, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object, object> target = callsite.Target;
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14, 12))
+                .Message;
+
+            //  The best overload for 'DoMoreStuff' does not have a parameter named 'nada'
+            Assert.Contains("'DoOtherStuff'", message);
+            Assert.Contains("'nada'", message);
+        }
+
+        [Fact]
+        public void NameOnlySecondWithSecondArgumentWrongPlace()
+        {
+            CallSite<Func<CallSite, object, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object, object> target = callsite.Target;
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14, 13))
+                .Message;
+
+            //  Named argument 'z' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'z'", message);
+        }
+
+        [Fact]
+        public void NameOnlySecondWithSecondArgumentNotFound()
+        {
+            CallSite<Func<CallSite, object, object, object, object, object>> callsite =
+                CallSite<Func<CallSite, object, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            Func<CallSite, object, object, object, object, object> target = callsite.Target;
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => target(callsite, new TypeWithMethods(), 9, 14, 12))
+                .Message;
+
+            //  The best overload for 'DoMoreStuff' does not have a parameter named 'nada'
+            Assert.Contains("'DoOtherStuff'", message);
+            Assert.Contains("'nada'", message);
+        }
+
+        class BaseClass
+        {
+            public virtual int Adder(int x, int y) => x + y;
+        }
+
+        class Derived : BaseClass
+        {
+            public override int Adder(int a, int b) => base.Adder(a, b);
+        }
+
+        [Fact]
+        public void OverrideChangesName()
+        {
+            // Static compilation behavior is to use the name of the type of the variable the object is accessed through.
+            // Dynamic behavior matches that when the argument is UseCompileTimeType
+            // Otherwise it depends on the actual type of the object.
+
+            // Defined in terms of base class. Using "x"
+            CallSite<Func<CallSite, BaseClass, int, int, object>> callSite =
+                CallSite<Func<CallSite, BaseClass, int, int, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            Assert.Equal(9, callSite.Target(callSite, new Derived(), 4, 5));
+            Assert.Equal(9, callSite.Target(callSite, new BaseClass(), 4, 5));
+
+            // Defined in terms of base class. Using "a"
+            callSite = CallSite<Func<CallSite, BaseClass, int, int, object>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                    }));
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, new Derived(), 4, 5))
+                .Message;
+
+            //  The best overload for 'Adder' does not have a parameter named 'a'
+            Assert.Contains("'Adder'", message);
+            Assert.Contains("'a'", message);
+            Assert.Equal(
+                message,
+                Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, new BaseClass(), 4, 5)).Message);
+
+            // Defined in terms of Derived, using "a"
+            CallSite<Func<CallSite, Derived, int, int, object>> callSiteDerived =
+                CallSite<Func<CallSite, Derived, int, int, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            Assert.Equal(9, callSiteDerived.Target(callSiteDerived, new Derived(), 4, 5));
+
+            // Defined in terms of Derived, using "x"
+            callSiteDerived = CallSite<Func<CallSite, Derived, int, int, object>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                    }));
+            message = Assert
+                .Throws<RuntimeBinderException>(() => callSiteDerived.Target(callSiteDerived, new Derived(), 4, 5))
+                .Message;
+
+            //  The best overload for 'Adder' does not have a parameter named 'x'
+            Assert.Contains("'Adder'", message);
+            Assert.Contains("'x'", message);
+
+            // Using runtime types, and "a"
+            CallSite<Func<CallSite, object, object, object, object>> callSiteRuntime =
+                CallSite<Func<CallSite, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                        }));
+
+            Assert.Equal(9, callSiteRuntime.Target(callSiteRuntime, new Derived(), 4, 5));
+            message = Assert
+                .Throws<RuntimeBinderException>(() => callSiteRuntime.Target(callSiteRuntime, new BaseClass(), 4, 5))
+                .Message;
+
+            //  The best overload for 'Adder' does not have a parameter named 'a'
+            Assert.Contains("'Adder'", message);
+            Assert.Contains("'a'", message);
+
+            // Using runtime types, and "x"
+            callSiteRuntime = CallSite<Func<CallSite, object, object, object, object>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                    }));
+
+            Assert.Equal(9, callSiteRuntime.Target(callSiteRuntime, new BaseClass(), 4, 5));
+            message = Assert
+                .Throws<RuntimeBinderException>(() => callSiteRuntime.Target(callSiteRuntime, new Derived(), 4, 5))
+                .Message;
+
+            //  The best overload for 'Adder' does not have a parameter named 'x'
+            Assert.Contains("'Adder'", message);
+            Assert.Contains("'x'", message);
+        }
+
+        // Tests based on the static tests for Roslyn.
+        class C1
+        {
+            static void M(int a, int b)
+            {
+                Console.Write($"First {a} {b}. ");
+            }
+
+            static void M(long b, long a)
+            {
+                Console.Write($"Second {b} {a}. ");
+            }
+        }
+
+        [Fact]
+        public void TestSimple()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int>> callsite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C1),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callsite.Target(callsite, typeof(C1), 1, 2);
+            callsite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C1),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a")
+                    }));
+            callsite.Target(callsite, typeof(C1), 3, 4);
+            Assert.Equal("First 1 2. Second 3 4. ", console.ToString());
+        }
+
+        class C2
+        {
+            C2(int a, int b)
+            {
+                Console.Write($"{a} {b}.");
+            }
+        }
+
+        [Fact]
+        public void TestSimpleConstructor()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Func<CallSite, Type, int, int, C2>> callsite = CallSite<Func<CallSite, Type, int, int, C2>>.Create(
+                Binder.InvokeConstructor(
+                    CSharpBinderFlags.None, typeof(C2),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callsite.Target(callsite, typeof(C2), 1, 2);
+            Assert.Equal("1 2.", console.ToString());
+        }
+
+        class C3
+        {
+            public delegate void MyDelegate(int a, int b);
+
+            public MyDelegate e;
+
+            static void M(int a, int b)
+            {
+                Console.Write($"{a} {b}. ");
+            }
+
+            public C3()
+            {
+                e += M;
+            }
+        }
+
+        [Fact]
+        public void TestSimpleDelegate()
+        {
+            // This test differs from its static equivalent considerably, as some event-related matters aren't as directly
+            // applicable. We can use it for checking both direct and deferred delegate invocation.
+
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            C3 targetObject = new C3();
+            CallSite<Func<CallSite, object, object>> getCallSite = CallSite<Func<CallSite, object, object>>.Create(
+                Binder.GetMember(
+                    CSharpBinderFlags.None, "e", typeof(C3),
+                    new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}));
+            object dele = getCallSite.Target(getCallSite, targetObject);
+            CallSite<Action<CallSite, object, int, int>> invokeCallSite =
+                CallSite<Action<CallSite, object, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "Invoke", Type.EmptyTypes, typeof(C3),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            invokeCallSite.Target(invokeCallSite, dele, 1, 2);
+            invokeCallSite = CallSite<Action<CallSite, object, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "e", Type.EmptyTypes, typeof(C3),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                    }));
+            invokeCallSite.Target(invokeCallSite, targetObject, 1, 2);
+            Assert.Equal("1 2. 1 2. ", console.ToString());
+        }
+
+        class C4
+        {
+            int this[int a, int b]
+            {
+                get
+                {
+                    Console.Write($"Get {a} {b}. ");
+                    return 0;
+                }
+                set { Console.Write($"Set {a} {b} {value}."); }
+            }
+        }
+
+        [Fact]
+        public void TestSimpleIndexer()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            C4 targetObject = new C4();
+            CallSite<Func<CallSite, object, int, int, object>> getCallSite =
+                CallSite<Func<CallSite, object, int, int, object>>.Create(
+                    Binder.GetIndex(
+                        CSharpBinderFlags.None, typeof(C4),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument,
+                                "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            Assert.Equal(0, getCallSite.Target(getCallSite, targetObject, 1, 2));
+            CallSite<Func<CallSite, object, int, int, int, object>> setCallSite =
+                CallSite<Func<CallSite, object, int, int, int, object>>.Create(
+                    Binder.SetIndex(
+                        CSharpBinderFlags.None, typeof(C4),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument,
+                                "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            Assert.Equal(5, setCallSite.Target(setCallSite, targetObject, 3, 4, 5));
+            Assert.Equal("Get 1 2. Set 3 4 5.", console.ToString());
+        }
+
+
+        class C5
+        {
+            public C5()
+            {
+            }
+
+            int this[int a, int b]
+            {
+                get { throw null; }
+            }
+
+            C5(int a, int b)
+            {
+            }
+
+            // In the static test this is a local function on Main, but to test that without hunting
+            // for the name (likely C.<Main>g__local|3_0, but that's implementation-dependent) to
+            // push in with reflection we need the support from the compiler that we have yet to make
+            // possible. Cut out the chicken-egg problem and just test on a non-local equivalent.
+            void Method(int a, int b)
+            {
+            }
+        }
+
+        [Fact]
+        public void TestSimpleError()
+        {
+            CallSite<Func<CallSite, Type, int, int, C5>> ctorCallsite =
+                CallSite<Func<CallSite, Type, int, int, C5>>.Create(
+                    Binder.InvokeConstructor(
+                        CSharpBinderFlags.None, typeof(C5),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument,
+                                "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => ctorCallsite.Target(ctorCallsite, typeof(C5), 1, 2))
+                .Message;
+
+            // Named argument 'b' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'b'", message);
+            CallSite<Func<CallSite, object, int, int, object>> getCallSite =
+                CallSite<Func<CallSite, object, int, int, object>>.Create(
+                    Binder.GetIndex(
+                        CSharpBinderFlags.None, typeof(C5),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument,
+                                "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        }));
+            message = Assert.Throws<RuntimeBinderException>(() => getCallSite.Target(getCallSite, new C5(), 1, 2))
+                .Message;
+            Assert.Contains("'b'", message);
+            CallSite<Func<CallSite, object, int, int>> invokeCallSite =
+                CallSite<Func<CallSite, object, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            message = Assert.Throws<RuntimeBinderException>(() => getCallSite.Target(invokeCallSite, new C5(), 1, 2))
+                .Message;
+            Assert.Contains("'b'", message);
+        }
+
+        class C6
+        {
+            static void M(int first, int other)
+            {
+                System.Console.Write($"{first} {other}");
+            }
+        }
+
+        [Fact]
+        public void TestPositionalUnaffected()
+        {
+            CallSite<Action<CallSite, Type, int, int>> invokeCallSite =
+                CallSite<Action<CallSite, Type, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.None, "M", Type.EmptyTypes, typeof(C6),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "first")
+                        }));
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => invokeCallSite.Target(invokeCallSite, typeof(C6), 1, 2))
+                .Message;
+
+            // Named argument 'first' specifies a parameter for which a positional argument has already been given
+            Assert.Contains("'first'", message);
+        }
+
+        class C7
+        {
+            static void M<T1, T2>(T1 a, T2 b)
+            {
+                System.Console.Write($"{a} {b}.");
+            }
+        }
+
+        [Fact]
+        public void TestGenericInference()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+
+            // Test with types defined fully.
+            CallSite<Action<CallSite, Type, int, string>> compileTimeTypeCallSite =
+                CallSite<Action<CallSite, Type, int, string>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", new[] {typeof(int), typeof(string)}, typeof(C7),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            compileTimeTypeCallSite.Target(compileTimeTypeCallSite, typeof(C7), 1, "hi");
+            Assert.Equal("1 hi.", console.ToString());
+
+            // Test with types defined fully in delegate but generic types deduced.
+            console.GetStringBuilder().Length = 0;
+            compileTimeTypeCallSite = CallSite<Action<CallSite, Type, int, string>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C7),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            compileTimeTypeCallSite.Target(compileTimeTypeCallSite, typeof(C7), 1, "hi");
+            Assert.Equal("1 hi.", console.ToString());
+
+            // Test with all types deduced by the dynamic binder.
+            console.GetStringBuilder().Length = 0;
+            CallSite<Action<CallSite, Type, object, object>> runtimeTypeCallSite =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C7),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            runtimeTypeCallSite.Target(runtimeTypeCallSite, typeof(C7), 1, "hi");
+            Assert.Equal("1 hi.", console.ToString());
+        }
+
+        class C8
+        {
+            static void M(int a, int b, int c = 1)
+            {
+                System.Console.Write($"M {a} {b}");
+            }
+        }
+
+        [Fact]
+        public void TestPositionalUnaffected2()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C8),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C8), 1, 2))
+                .Message;
+
+            //  Named argument 'c' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'c'", message);
+        }
+
+        class C9
+        {
+            static void M(params int[] x)
+            {
+            }
+        }
+
+        [Fact]
+        public void TestNamedParams()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C9), 1, 2))
+                .Message;
+
+            // No overload for method 'M' takes 2 arguments
+            Assert.Contains("'M'", message);
+            Assert.Contains(" 2 ", message);
+        }
+
+        [Fact]
+        public void TestNamedParams2()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x")
+                    }));
+
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C9), 1, 2))
+                .Message;
+
+            // Named argument 'x' specifies a parameter for which a positional argument has already been given
+            Assert.Contains("'x'", message);
+        }
+
+        class C10
+        {
+            static void M(int x, params string[] y)
+            {
+                System.Console.Write($"{x} {string.Join(",", y)}. ");
+            }
+        }
+
+        [Fact]
+        public void TestNamedParamsVariousForms()
+        {
+            // This extends the static test in also calling with no arguments for the params section.
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+
+            CallSite<Action<CallSite, Type, int, string>> callSite0 =
+                CallSite<Action<CallSite, Type, int, string>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "x"),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y")
+                        }));
+            callSite0.Target(callSite0, typeof(C10), 1, "2");
+
+            callSite0 = CallSite<Action<CallSite, Type, int, string>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callSite0.Target(callSite0, typeof(C10), 2, "3");
+
+            CallSite<Action<CallSite, Type, int, string[]>> callSite1 =
+                CallSite<Action<CallSite, Type, int, string[]>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            callSite1.Target(callSite1, typeof(C10), 3, new[] {"4", "5"});
+
+            CallSite<Action<CallSite, Type, int>> callSite2 = CallSite<Action<CallSite, Type, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x")
+                    }));
+            callSite2.Target(callSite2, typeof(C10), 4);
+
+            Assert.Equal("1 2. 2 3. 3 4,5. 4 . ", console.ToString());
+        }
+
+        [Fact]
+        public void TestNamedParamsVariousFormsDynamicallyDeducedTypes()
+        {
+            // This extends the static test in also calling with no arguments for the params section.
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+
+            CallSite<Action<CallSite, Type, object, object>> callSite0 =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "y")
+                        }));
+            callSite0.Target(callSite0, typeof(C10), 1, "2");
+
+            callSite0 = CallSite<Action<CallSite, Type, object, object>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                    }));
+            callSite0.Target(callSite0, typeof(C10), 2, "3");
+
+            CallSite<Action<CallSite, Type, object, object>> callSite1 =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            callSite1.Target(callSite1, typeof(C10), 3, new[] {"4", "5"});
+
+            CallSite<Action<CallSite, Type, object>> callSite2 = CallSite<Action<CallSite, Type, object>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x")
+                    }));
+            callSite2.Target(callSite2, typeof(C10), 4);
+
+            Assert.Equal("1 2. 2 3. 3 4,5. 4 . ", console.ToString());
+        }
+
+        [Fact]
+        public void TestTwiceNamedParams()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x")
+                    }));
+
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C9), 1, 2))
+                .Message;
+
+            // Named argument 'x' cannot be specified multiple times
+            Assert.Contains("'x'", message);
+        }
+
+        class C11
+        {
+            static void M(int x, params int[] y)
+            {
+            }
+        }
+
+        [Fact]
+        public void TestNamedParams3()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C11),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C11), 1, 2))
+                .Message;
+
+            // Named argument 'y' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains("'y'", message);
+        }
+
+        [Fact]
+        public void TestNamedParams4()
+        {
+            CallSite<Action<CallSite, Type, int, int, int>> callSite =
+                CallSite<Action<CallSite, Type, int, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C11),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "x"),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "y"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C11), 1, 2, 3))
+                .Message;
+
+            // No overload for method 'M' takes 3 arguments
+            Assert.Contains("'M'", message);
+            Assert.Contains(" 3 ", message);
+        }
+
+        class C12
+        {
+            static void M(int x, params int[] y)
+            {
+                System.Console.Write($"x={x} y[0]={y[0]} y.Length={y.Length}");
+            }
+        }
+
+        [Fact]
+        public void TestNamedParams5()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C12),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y"),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x")
+                    }));
+            callSite.Target(callSite, typeof(C12), 1, 2);
+            Assert.Equal("x=2 y[0]=1 y.Length=1", console.ToString());
+        }
+
+        class C13
+        {
+            static void M(int a = 1, int b = 2, int c = 3)
+            {
+            }
+        }
+
+        [Fact]
+        public void TestBadNonTrailing()
+        {
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C13),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            string message = Assert.Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C13), 3, 2)).Message;
+            //Named argument 'c' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains(" 'c' ", message);
+        }
+
+        class C14
+        {
+            static void M(int a = 1, int b = 2, int c = 3)
+            {
+            }
+            static void M(long c = 1, long b = 2)
+            {
+                System.Console.Write($"Second {c} {b}. ");
+            }
+        }
+
+        [Fact]
+        public void TestPickGoodOverload()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C14),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callSite.Target(callSite, typeof(C14), 3, 2);
+            Assert.Equal("Second 3 2. ", console.ToString());
+        }
+
+        [Fact]
+        public void TestPickGoodOverloadDynamicallyTyped()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, object, object>> callSite =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C14),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "c"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            callSite.Target(callSite, typeof(C14), 3, 2);
+            Assert.Equal("Second 3 2. ", console.ToString());
+        }
+
+        class C15
+        {
+            static void M(long a = 1, long b = 2, long c = 3)
+            {
+            }
+            static void M(int c = 1, int b = 2)
+            {
+                System.Console.Write($"Second {c} {b}.");
+            }
+        }
+
+        [Fact]
+        public void TestPickGoodOverload2()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C15),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callSite.Target(callSite, typeof(C15), 3, 2);
+            Assert.Equal("Second 3 2.", console.ToString());
+        }
+
+        [Fact]
+        public void TestPickGoodOverload2DynamicallyTyped()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, object, object>> callSite =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C15),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "c"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            callSite.Target(callSite, typeof(C15), 3, 2);
+            Assert.Equal("Second 3 2.", console.ToString());
+        }
+
+        class C16
+        {
+            static void M(int a, int b, int c = 42)
+            {
+                System.Console.Write(c);
+            }
+        }
+
+        [Fact]
+        public void TestOptionalValues()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int>> callSite = CallSite<Action<CallSite, Type, int, int>>.Create(
+                Binder.InvokeMember(
+                    CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C16),
+                    new[]
+                    {
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                        CSharpArgumentInfo.Create(
+                            CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"),
+                        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                    }));
+            callSite.Target(callSite, typeof(C16), 1, 2);
+            Assert.Equal("42", console.ToString());
+        }
+
+        [Fact]
+        public void TestOptionalValuesRunTimeTypes()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, object, object>> callSite =
+                CallSite<Action<CallSite, Type, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C16),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            callSite.Target(callSite, typeof(C16), 1, 2);
+            Assert.Equal("42", console.ToString());
+        }
+
+        class C17
+        {
+            static void M(int a, int b, params int[] c)
+            {
+            }
+        }
+
+        [Fact]
+        public void TestParams()
+        {
+            CallSite<Action<CallSite, Type, int, int, int>> callSite =
+                CallSite<Action<CallSite, Type, int, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C17),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C17), 2, 3, 4))
+                .Message;
+            // Named argument 'b' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains(" 'b' ", message);
+        }
+
+        [Fact]
+        public void TestParamsRuntimeTypes()
+        {
+            CallSite<Action<CallSite, Type, object, object, object>> callSite =
+                CallSite<Action<CallSite, Type, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C17),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            string message = Assert
+                .Throws<RuntimeBinderException>(() => callSite.Target(callSite, typeof(C17), 2, 3, 4))
+                .Message;
+
+            // Named argument 'b' is used out-of-position but is followed by an unnamed argument
+            Assert.Contains(" 'b' ", message);
+        }
+
+        class C18
+        {
+            static void M(int a, int b, params int[] c)
+            {
+                System.Console.Write($"{a} {b} {c[0]} {c[1]} Length:{c.Length}");
+            }
+        }
+
+        [Fact]
+        public void TestParams2()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, int, int, int, int>> callSite =
+                CallSite<Action<CallSite, Type, int, int, int, int>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C18),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(
+                                CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType,
+                                "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
+                        }));
+            callSite.Target(callSite, typeof(C18), 1, 2, 3, 4);
+            Assert.Equal("1 2 3 4 Length:2", console.ToString());
+        }
+
+        [Fact]
+        public void TestParams2RuntimeTypes()
+        {
+            StringWriter console = new StringWriter();
+            Console.SetOut(console);
+            CallSite<Action<CallSite, Type, object, object, object, object>> callSite =
+                CallSite<Action<CallSite, Type, object, object, object, object>>.Create(
+                    Binder.InvokeMember(
+                        CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C18),
+                        new[]
+                        {
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+                        }));
+            callSite.Target(callSite, typeof(C18), 1, 2, 3, 4);
+            Assert.Equal("1 2 3 4 Length:2", console.ToString());
+        }
+    }
+}