From 29fccdb78d38e1b4f9352dea99273c93f713a24a Mon Sep 17 00:00:00 2001
From: Daniel Abrahamsson <daniel.abrahamsson@prover.com>
Date: Wed, 30 Sep 2020 07:47:49 +0200
Subject: [PATCH] Select correct method to invoke when keyword arguments are
 used

If there were two methods with the same name, but with different arity,
`Foo(a)` and `Foo(a,b)`, and the latter was called with a keyword
argument `some.Foo(0, b=1)`, `Foo(0)` would be called instead of
`Foo(0,1)`.

Fixes #1235.
---
 CHANGELOG.md                     | 1 +
 src/runtime/constructorbinder.cs | 2 +-
 src/runtime/methodbinder.cs      | 2 +-
 src/testing/methodtest.cs        | 6 ++++++
 src/tests/test_method.py         | 7 +++++++
 5 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0559c43c..0c48ecb90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ details about the cause of the failure
 -    Fix incorrect dereference in params array handling
 -    Fix `object[]` parameters taking precedence when should not in overload resolution
 -    Fixed a bug where all .NET class instances were considered Iterable
+-    Fix incorrect choice of method to invoke when using keyword arguments.
 
 ## [2.5.0][] - 2020-06-14
 
diff --git a/src/runtime/constructorbinder.cs b/src/runtime/constructorbinder.cs
index 0f9806c0e..973707f02 100644
--- a/src/runtime/constructorbinder.cs
+++ b/src/runtime/constructorbinder.cs
@@ -89,7 +89,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
                 // any extra args are intended for the subclass' __init__.
 
                 IntPtr eargs = Runtime.PyTuple_New(0);
-                binding = Bind(inst, eargs, kw);
+                binding = Bind(inst, eargs, IntPtr.Zero);
                 Runtime.XDecref(eargs);
 
                 if (binding == null)
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 49bf5b171..df8579ca4 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -580,7 +580,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
             var match = false;
             paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false;
 
-            if (positionalArgumentCount == parameters.Length)
+            if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0)
             {
                 match = true;
             }
diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs
index 9a4c408d6..30e42ac9f 100644
--- a/src/testing/methodtest.cs
+++ b/src/testing/methodtest.cs
@@ -699,6 +699,12 @@ public string PublicMethod(string echo)
             return echo;
         }
     }
+
+    public class MethodArityTest
+    {
+        public string Foo(int a) { return "Arity 1"; }
+        public string Foo(int a, int b) { return "Arity 2"; }
+    }
 }
 
 namespace PlainOldNamespace
diff --git a/src/tests/test_method.py b/src/tests/test_method.py
index c9fc89c7c..a358025a5 100644
--- a/src/tests/test_method.py
+++ b/src/tests/test_method.py
@@ -1149,3 +1149,10 @@ def test_optional_and_default_params():
 
     res = MethodTest.OptionalAndDefaultParams2(b=2, c=3)
     assert res == "0232"
+
+def test_keyword_arg_method_resolution():
+    from Python.Test import MethodArityTest
+
+    ob = MethodArityTest()
+    assert ob.Foo(1, b=2) == "Arity 2"
+