patch 8.2.2172: Vim9: number of arguments is not always checked

Problem:    Vim9: number of arguments is not always checked. (Yegappan
            Lakshmanan)
Solution:   Check number of arguments when calling function by name.
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 787d010..c79c101 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -18,6 +18,7 @@
 void funcdepth_decrement(void);
 int funcdepth_get(void);
 void funcdepth_restore(int depth);
+int check_user_func_argcount(ufunc_T *fp, int argcount);
 int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
 void save_funccal(funccal_entry_T *entry);
 void restore_funccal(void);
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 61e909b..4a0c7a9 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -470,6 +470,25 @@
   delete('Xscript')
 enddef
 
+def Test_call_funcref_wrong_args()
+  var head =<< trim END
+      vim9script
+      def Func3(a1: string, a2: number, a3: list<number>)
+        echo a1 .. a2 .. a3[0]
+      enddef
+      def Testme()
+        var funcMap: dict<func> = {func: Func3}
+  END
+  var tail =<< trim END
+      enddef
+      Testme()
+  END
+  CheckScriptSuccess(head + ["funcMap['func']('str', 123, [1, 2, 3])"] + tail)
+
+  CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:')
+  CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:')
+enddef
+
 def Test_call_lambda_args()
   CheckDefFailure(['echo {i -> 0}()'],
                   'E119: Not enough arguments for function: {i -> 0}()')
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index b0e7635..5406391 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -1312,12 +1312,12 @@
   # FuncNo() is not redefined
   writefile(first_lines + nono_lines, 'Xreloaded.vim')
   source Xreloaded.vim
-  g:DoCheck()
+  g:DoCheck(false)
 
   # FuncNo() is back
   writefile(first_lines + withno_lines, 'Xreloaded.vim')
   source Xreloaded.vim
-  g:DoCheck()
+  g:DoCheck(false)
 
   delete('Xreloaded.vim')
 enddef
diff --git a/src/userfunc.c b/src/userfunc.c
index b9fb644..f7ad9f3 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1834,6 +1834,22 @@
 }
 
 /*
+ * Check the argument count for user function "fp".
+ * Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise.
+ */
+    int
+check_user_func_argcount(ufunc_T *fp, int argcount)
+{
+    int regular_args = fp->uf_args.ga_len;
+
+    if (argcount < regular_args - fp->uf_def_args.ga_len)
+	return FCERR_TOOFEW;
+    else if (!has_varargs(fp) && argcount > regular_args)
+	return FCERR_TOOMANY;
+    return FCERR_UNKNOWN;
+}
+
+/*
  * Call a user function after checking the arguments.
  */
     int
@@ -1846,15 +1862,13 @@
 	dict_T	    *selfdict)
 {
     int error;
-    int regular_args = fp->uf_args.ga_len;
 
     if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
 	*funcexe->doesrange = TRUE;
-    if (argcount < regular_args - fp->uf_def_args.ga_len)
-	error = FCERR_TOOFEW;
-    else if (!has_varargs(fp) && argcount > regular_args)
-	error = FCERR_TOOMANY;
-    else if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
+    error = check_user_func_argcount(fp, argcount);
+    if (error != FCERR_UNKNOWN)
+	return error;
+    if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
 	error = FCERR_DICT;
     else
     {
diff --git a/src/version.c b/src/version.c
index d149c9b..f374d43 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2172,
+/**/
     2171,
 /**/
     2170,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 6409d88..606ce0c 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -606,6 +606,17 @@
 	return FAIL;
     if (ufunc->uf_def_status == UF_COMPILED)
     {
+	int error = check_user_func_argcount(ufunc, argcount);
+
+	if (error != FCERR_UNKNOWN)
+	{
+	    if (error == FCERR_TOOMANY)
+		semsg(_(e_toomanyarg), ufunc->uf_name);
+	    else
+		semsg(_(e_toofewarg), ufunc->uf_name);
+	    return FAIL;
+	}
+
 	// The function has been compiled, can call it quickly.  For a function
 	// that was defined later: we can call it directly next time.
 	if (iptr != NULL)