From d3627812107baae3f2762db95cb99ec8ce0531ca Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sun, 12 Jan 2025 10:10:15 -0500
Subject: [PATCH 26/48] {needs work} c++: UX improvements to 'too {few,many}
 arguments' errors [PR118112]

---
 gcc/cp/typeck.cc                          | 104 +++++++++++++++++-----
 gcc/testsuite/g++.dg/cpp0x/variadic169.C  |   2 +-
 gcc/testsuite/g++.dg/modules/macloc-1_c.C |   4 +-
 gcc/testsuite/g++.dg/modules/macloc-1_d.C |   4 +-
 gcc/testsuite/gcc.dg/too-few-arguments.c  |  18 +++-
 gcc/testsuite/gcc.dg/too-many-arguments.c |  67 ++++++++------
 6 files changed, 142 insertions(+), 57 deletions(-)

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 79eb3b5ba28..426bf68da61 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -59,7 +59,7 @@ static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t);
 static void casts_away_constness_r (tree *, tree *, tsubst_flags_t);
 static bool casts_away_constness (tree, tree, tsubst_flags_t);
 static bool maybe_warn_about_returning_address_of_local (tree, location_t = UNKNOWN_LOCATION);
-static void error_args_num (location_t, tree, bool);
+static void error_args_num (location_t, tree, int, int, bool);
 static int convert_arguments (tree, vec<tree, va_gc> **, tree, int,
                               tsubst_flags_t);
 static bool is_std_move_p (tree);
@@ -4786,11 +4786,19 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
 }
 
 /* Subroutine of convert_arguments.
-   Print an error message about a wrong number of arguments.  */
+   Print an error message about a wrong number of arguments.
+   If AT_LEAST_P is true, then EXPECTED_NUM is the minimum number
+   of expected arguments, otherwise EXPECTED_NUM is the exact number
+   of expected arguments.
+   ACTUAL_NUM is the actual number of arguments that were explicitly
+   passed at the callsite (i.e. not counting default arguments).  */
 
 static void
-error_args_num (location_t loc, tree fndecl, bool too_many_p)
+error_args_num (location_t loc, tree fndecl, int expected_num, int actual_num,
+		bool at_least_p)
 {
+  gcc_assert (expected_num != actual_num);
+  const bool too_many_p = actual_num > expected_num;
   if (fndecl)
     {
       auto_diagnostic_group d;
@@ -4801,22 +4809,28 @@ error_args_num (location_t loc, tree fndecl, bool too_many_p)
 		  == DECL_NAME (TYPE_NAME (DECL_CONTEXT (fndecl)))))
 	    error_at (loc,
 		      too_many_p
-		      ? G_("too many arguments to constructor %q#D")
-		      : G_("too few arguments to constructor %q#D"),
-		      fndecl);
+		      ? G_("too many arguments to constructor %q#D; expected %i, have %i")
+		      : (at_least_p
+			 ? G_("too few arguments to constructor %q#D; expected at least %i, have %i")
+			 : G_("too few arguments to constructor %q#D; expected %i, have %i")),
+		      fndecl, expected_num, actual_num);
 	  else
 	    error_at (loc,
 		      too_many_p
-		      ? G_("too many arguments to member function %q#D")
-		      : G_("too few arguments to member function %q#D"),
-		      fndecl);
+		      ? G_("too many arguments to member function %q#D; expected %i, have %i")
+		      : (at_least_p
+			 ? G_("too few arguments to member function %q#D; expected at least %i, have %i")
+			 : G_("too few arguments to member function %q#D; expected %i, have %i")),
+		      fndecl, expected_num, actual_num);
 	}
       else
 	error_at (loc,
 		  too_many_p
-		  ? G_("too many arguments to function %q#D")
-		  : G_("too few arguments to function %q#D"),
-		  fndecl);
+		  ? G_("too many arguments to function %q#D; expected %i, have %i")
+		  : (at_least_p
+		     ? G_("too few arguments to function %q#D; expected at least %i, have %i")
+		     : G_("too few arguments to function %q#D; expected %i, have %i")),
+		  fndecl, expected_num, actual_num);
       if (!DECL_IS_UNDECLARED_BUILTIN (fndecl))
 	inform (DECL_SOURCE_LOCATION (fndecl), "declared here");
     }
@@ -4825,12 +4839,19 @@ error_args_num (location_t loc, tree fndecl, bool too_many_p)
       if (c_dialect_objc ()  &&  objc_message_selector ())
 	error_at (loc,
 		  too_many_p
-		  ? G_("too many arguments to method %q#D")
-		  : G_("too few arguments to method %q#D"),
-		  objc_message_selector ());
+		  ? G_("too many arguments to method %q#D; expected %i, have %i")
+		  : (at_least_p
+		     ? G_("too few arguments to method %q#D; expected at least %i, have %i")
+		     : G_("too few arguments to method %q#D; expected %i, have %i")),
+		  objc_message_selector (), expected_num, actual_num);
       else
-	error_at (loc, too_many_p ? G_("too many arguments to function")
-		                  : G_("too few arguments to function"));
+	error_at (loc,
+		  too_many_p
+		  ? G_("too many arguments to function; expected %i, have %i")
+		  : (at_least_p
+		     ? G_("too few arguments to function; expected at least %i, have %i")
+		     : G_("too few arguments to function; expected %i, have %i")),
+		  expected_num, actual_num);
     }
 }
 
@@ -4860,9 +4881,11 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
   /* Argument passing is always copy-initialization.  */
   flags |= LOOKUP_ONLYCONVERTING;
 
-  for (i = 0, typetail = typelist;
-       i < vec_safe_length (*values);
-       i++)
+  /* Preserve actual number of arguments passed (without counting default
+     args), in case we need to complain about too many/few.  */
+  const unsigned actual_num = vec_safe_length (*values);
+
+  for (i = 0, typetail = typelist; i < actual_num; i++)
     {
       tree type = typetail ? TREE_VALUE (typetail) : 0;
       tree val = (**values)[i];
@@ -4873,7 +4896,10 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
       if (type == void_type_node)
 	{
           if (complain & tf_error)
-	    error_args_num (input_location, fndecl, /*too_many_p=*/true);
+	    /* Too many args.  */
+	    error_args_num (input_location, fndecl,
+			    /*expected_num=*/i, actual_num,
+			    /*at_least_p=*/false);
 	  return -1;
 	}
 
@@ -4992,7 +5018,41 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
       if (typetail && typetail != void_list_node)
 	{
 	  if (complain & tf_error)
-	    error_args_num (input_location, fndecl, /*too_many_p=*/false);
+	    {
+	      /* Not enough args.
+		 Determine minimum number of arguments required.  */
+	      int min_expected_num = 0;
+	      bool at_least_p = false;
+	      tree iter = typelist;
+	      while (true)
+		{
+		  if (!iter)
+		    {
+		      /* Variadic arguments; stop iterating.  */
+		      at_least_p = true;
+		      break;
+		    }
+		  if (iter == void_list_node)
+		    /* End of arguments; stop iterating.  */
+		    break;
+		  if (fndecl && TREE_PURPOSE (iter))
+		    {
+		      /* Found a default argument; skip this one when
+			 counting minimum required, but there might
+			 be non-default arguments left to count:
+			 after DR777, with explicit template args we can
+			 end up with a default argument followed by
+			 no default argument.  */
+		      at_least_p = true;
+		      iter = TREE_CHAIN (iter);
+		      continue;
+		    }
+		  ++min_expected_num;
+		  iter = TREE_CHAIN (iter);
+		}
+	      error_args_num (input_location, fndecl,
+			      min_expected_num, actual_num, at_least_p);
+	    }
 	  return -1;
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic169.C b/gcc/testsuite/g++.dg/cpp0x/variadic169.C
index 6858973cd2e..460bb3b8a19 100644
--- a/gcc/testsuite/g++.dg/cpp0x/variadic169.C
+++ b/gcc/testsuite/g++.dg/cpp0x/variadic169.C
@@ -5,5 +5,5 @@ template<typename ...T> void f(int n = 0, T ...t);
 
 int main()
 {
-  f<int>();			// { dg-error "too few arguments" }
+  f<int>();			// { dg-error "too few arguments to function '\[^\n\r\]*'; expected at least 1, have 0" }
 }
diff --git a/gcc/testsuite/g++.dg/modules/macloc-1_c.C b/gcc/testsuite/g++.dg/modules/macloc-1_c.C
index 5865a34687e..3f980c21381 100644
--- a/gcc/testsuite/g++.dg/modules/macloc-1_c.C
+++ b/gcc/testsuite/g++.dg/modules/macloc-1_c.C
@@ -8,6 +8,6 @@ void gru ()
   you (1);
 }
 
-// { dg-regexp "\[^\n]*macloc-1_c.C:7:6: error: too many arguments to function 'int me@agnes\\(\\)'\nIn module agnes, imported at \[^\n]*macloc-1_b.C:8,\nof module edith, imported at \[^\n]*macloc-1_c.C:3:\n\[^\n]*macloc-1_a.C:11:12: note: declared here\n\[^\n]*macloc-1_a.C:8:20: note: in definition of macro 'BOB'\n" }
+// { dg-regexp "\[^\n]*macloc-1_c.C:7:6: error: too many arguments to function 'int me@agnes\\(\\)'; expected 0, have 1\nIn module agnes, imported at \[^\n]*macloc-1_b.C:8,\nof module edith, imported at \[^\n]*macloc-1_c.C:3:\n\[^\n]*macloc-1_a.C:11:12: note: declared here\n\[^\n]*macloc-1_a.C:8:20: note: in definition of macro 'BOB'\n" }
 
-// { dg-regexp "\[^\n]*macloc-1_c.C:8:7: error: too many arguments to function 'int you@agnes\\(\\)'\nIn module agnes, imported at \[^\n]*macloc-1_b.C:8,\nof module edith, imported at \[^\n]*macloc-1_c.C:3:\n\[^\n]*macloc-1_a.C:12:14: note: declared here\n\[^\n]*macloc-1_a.C:9:22: note: in definition of macro 'KEVIN'\n" }
+// { dg-regexp "\[^\n]*macloc-1_c.C:8:7: error: too many arguments to function 'int you@agnes\\(\\)'; expected 0, have 1\nIn module agnes, imported at \[^\n]*macloc-1_b.C:8,\nof module edith, imported at \[^\n]*macloc-1_c.C:3:\n\[^\n]*macloc-1_a.C:12:14: note: declared here\n\[^\n]*macloc-1_a.C:9:22: note: in definition of macro 'KEVIN'\n" }
diff --git a/gcc/testsuite/g++.dg/modules/macloc-1_d.C b/gcc/testsuite/g++.dg/modules/macloc-1_d.C
index 282a31c4a2d..56c001fc3f8 100644
--- a/gcc/testsuite/g++.dg/modules/macloc-1_d.C
+++ b/gcc/testsuite/g++.dg/modules/macloc-1_d.C
@@ -9,5 +9,5 @@ void margo ()
   gru (2);
 }
 
-// { dg-regexp "\[^\n]*macloc-1_d.C:8:6: error: too many arguments to function 'int me@agnes\\(\\)'\nIn module agnes, imported at \[^\n]*macloc-1_d.C:4:\n\[^\n]*macloc-1_a.C:11:12: note: declared here\n\[^\n]*macloc-1_a.C:8:20: note: in definition of macro 'BOB'\n" }
-// { dg-regexp "\[^\n]*macloc-1_d.C:9:7: error: too many arguments to function 'void gru@edith\\(\\)'\nIn module edith, imported at \[^\n]*macloc-1_d.C:3:\n\[^\n]*macloc-1_b.C:10:20: note: declared here\n\[^\n]*macloc-1_b.C:6:19: note: in definition of macro 'STUART'\n" }
+// { dg-regexp "\[^\n]*macloc-1_d.C:8:6: error: too many arguments to function 'int me@agnes\\(\\)'; expected 0, have 1\nIn module agnes, imported at \[^\n]*macloc-1_d.C:4:\n\[^\n]*macloc-1_a.C:11:12: note: declared here\n\[^\n]*macloc-1_a.C:8:20: note: in definition of macro 'BOB'\n" }
+// { dg-regexp "\[^\n]*macloc-1_d.C:9:7: error: too many arguments to function 'void gru@edith\\(\\)'; expected 0, have 1\nIn module edith, imported at \[^\n]*macloc-1_d.C:3:\n\[^\n]*macloc-1_b.C:10:20: note: declared here\n\[^\n]*macloc-1_b.C:6:19: note: in definition of macro 'STUART'\n" }
diff --git a/gcc/testsuite/gcc.dg/too-few-arguments.c b/gcc/testsuite/gcc.dg/too-few-arguments.c
index 4406e021e0c..633dccfeaf2 100644
--- a/gcc/testsuite/gcc.dg/too-few-arguments.c
+++ b/gcc/testsuite/gcc.dg/too-few-arguments.c
@@ -1,6 +1,10 @@
 extern void fn_a (void);
 extern void fn_b (int); /* { dg-message "declared here" } */
 extern void fn_c (int, int); /* { dg-message "declared here" } */
+#ifdef __cplusplus
+extern void fn_d (int, int=42); /* { dg-message "declared here" "" { target c++ } } */
+extern void fn_e (int, int=42, int=1066); /* { dg-message "declared here" "" { target c++ } } */
+#endif
 extern void fn_f (const char *, ...); /* { dg-message "declared here" } */
 
 void test_known_fn (void)
@@ -8,21 +12,27 @@ void test_known_fn (void)
   fn_a ();
   fn_b ();  /* { dg-error "too few arguments to function '\[^\n\r\]*'; expected 1, have 0" } */
   fn_c (42);/* { dg-error "too few arguments to function '\[^\n\r\]*'; expected 2, have 1" } */
+#ifdef __cplusplus
+  fn_d ();  /* { dg-error "too few arguments to function '\[^\n\r\]*'; expected at least 1, have 0" "" { target c++ } } */
+  fn_e ();  /* { dg-error "too few arguments to function '\[^\n\r\]*'; expected at least 1, have 0" "" { target c++ } } */
+#endif
   fn_f ();  /* { dg-error "too few arguments to function '\[^\n\r\]*'; expected at least 1, have 0" } */
 }
 
 struct foo
 {
   void (*callback_a) (void);
-  void (*callback_b) (int); /* { dg-message "declared here" } */
-  void (*callback_c) (int, int); /* { dg-message "declared here" } */
+  void (*callback_b) (int); /* { dg-message "declared here" "" { target c } } */
+  void (*callback_c) (int, int); /* { dg-message "declared here" "" { target c } } */
 };
 
 void test_callback (struct foo *f)
 {
   f->callback_a ();
   
-  f->callback_b (); /* { dg-error "too few arguments to function 'f->callback_b'; expected 1, have 0" } */
+  f->callback_b (); /* { dg-error "too few arguments to function 'f->callback_b'; expected 1, have 0" "" { target c } } */
+  /* { dg-error "too few arguments to function; expected 1, have 0" "" { target c++ } .-1 } */
 
-  f->callback_c (42); /* { dg-error "too few arguments to function 'f->callback_c'; expected 2, have 1" } */
+  f->callback_c (42); /* { dg-error "too few arguments to function 'f->callback_c'; expected 2, have 1" "" { target c } } */
+  /* { dg-error "too few arguments to function; expected 2, have 1" "" { target c++ } .-1 } */
 }
diff --git a/gcc/testsuite/gcc.dg/too-many-arguments.c b/gcc/testsuite/gcc.dg/too-many-arguments.c
index 5bbd4a306c3..5189e964032 100644
--- a/gcc/testsuite/gcc.dg/too-many-arguments.c
+++ b/gcc/testsuite/gcc.dg/too-many-arguments.c
@@ -1,83 +1,98 @@
-/* Ensure that we get an error on the call to fn_a with an
-   int arg below.  */
-/* { dg-additional-options "-std=c23" } */
+/* FIXME: move this and the other test to c-c++-common  */
 
-/* Verify that the first excess param is underlined.  */
-/* { dg-additional-options "-fdiagnostics-show-caret" } */
+/* For C, verify that the first excess param is underlined.
+   For C++ the calls location covers all the params, so we
+   can't underline individual params.  */
+/* { dg-additional-options "-fdiagnostics-show-caret" { target c } } */
 
 extern void fn_a (); /* { dg-message "declared here" } */
 extern void fn_b (void); /* { dg-message "declared here" } */
 extern void fn_c (int); /* { dg-message "declared here" } */
+#ifdef __cplusplus
+extern void fn_d (int, int=42); /* { dg-message "declared here" "" { target c++ } } */
+#endif
 
 void test_known_fn (void)
 {
-  fn_a (42); /* { dg-error "too many arguments to function 'fn_a'; expected 0, have 1" } */
+  fn_a (42); /* { dg-error "too many arguments to function 'fn_a'; expected 0, have 1" "" { target c } } */
+  /* { dg-error "too many arguments to function 'void fn_a\\(\\)'; expected 0, have 1" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    fn_a (42);
    ^~~~  ~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
  extern void fn_a ();
              ^~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
 
-  fn_b (1776); /* { dg-error "too many arguments to function 'fn_b'; expected 0, have 1" } */
+  fn_b (1776); /* { dg-error "too many arguments to function 'fn_b'; expected 0, have 1" "" { target c } } */
+  /* { dg-error "too many arguments to function 'void fn_b\\(\\)'; expected 0, have 1" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    fn_b (1776);
    ^~~~  ~~~~
-   { dg-end-multiline-output "" } */
+   { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
  extern void fn_b (void);
              ^~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
 
-  fn_c (1066, 1649);  /* { dg-error "too many arguments to function 'fn_c'; expected 1, have 2" } */
+  fn_c (1066, 1649);  /* { dg-error "too many arguments to function 'fn_c'; expected 1, have 2" "" { target c } } */
+  /* { dg-error "too many arguments to function 'void fn_c\\(int\\)'; expected 1, have 2" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    fn_c (1066, 1649);
    ^~~~        ~~~~
-   { dg-end-multiline-output "" } */
+   { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
  extern void fn_c (int);
              ^~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
+
+#ifdef __cplusplus
+  fn_d (1066);
+  fn_d (1066, 1649);
+  fn_d (1066, 1649, 1776); /* { dg-error "too many arguments to function '\[^\n\r\]*'; expected 2, have 3" "" { target c++ } } */
+#endif
 }
 
 struct foo
 {
-  void (*callback_a)(); /* { dg-message "declared here" } */
-  void (*callback_b)(void); /* { dg-message "declared here" } */
-  void (*callback_c)(int); /* { dg-message "declared here" } */
+  void (*callback_a)(); /* { dg-message "declared here" "" { target c } } */
+  void (*callback_b)(void); /* { dg-message "declared here" "" { target c } } */
+  void (*callback_c)(int); /* { dg-message "declared here" "" { target c } } */
 };
 
 void test_callback (struct foo *f)
 {
-  f->callback_a (42); /* { dg-error "too many arguments to function 'f->callback_a'; expected 0, have 1" } */
+  f->callback_a (42); /* { dg-error "too many arguments to function 'f->callback_a'; expected 0, have 1" "" { target c } } */
+  /* { dg-error "too many arguments to function; expected 0, have 1" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    f->callback_a (42);
    ^              ~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
    void (*callback_a)();
           ^~~~~~~~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
   
-  f->callback_b (1776); /* { dg-error "too many arguments to function 'f->callback_b'; expected 0, have 1" } */
+  f->callback_b (1776); /* { dg-error "too many arguments to function 'f->callback_b'; expected 0, have 1" "" { target c } } */
+  /* { dg-error "too many arguments to function; expected 0, have 1" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    f->callback_b (1776);
    ^              ~~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
    void (*callback_b)(void);
           ^~~~~~~~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
 
-  f->callback_c (1066, 1649); /* { dg-error "too many arguments to function 'f->callback_c'; expected 1, have 2" } */
+  f->callback_c (1066, 1649); /* { dg-error "too many arguments to function 'f->callback_c'; expected 1, have 2" "" { target c } } */
+  /* { dg-error "too many arguments to function; expected 1, have 2" "" { target c++ } .-1 } */
   /* { dg-begin-multiline-output "" }
    f->callback_c (1066, 1649);
    ^                    ~~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
   /* { dg-begin-multiline-output "" }
    void (*callback_c)(int);
           ^~~~~~~~~~
-     { dg-end-multiline-output "" } */
+     { dg-end-multiline-output "" { target c } } */
 }
-- 
2.49.0

