public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: David Malcolm <dmalcolm@redhat.com>
To: Jason Merrill <jason@redhat.com>,
	gcc-patches@gcc.gnu.org, Marek Polacek <polacek@redhat.com>
Subject: [PATCH v4] c/c++: UX improvements to 'too {few,many} arguments' errors [PR118112]
Date: Fri, 10 Jan 2025 13:47:43 -0500	[thread overview]
Message-ID: <f34fdf57159821af93c7da1643edfa4796a77ad4.camel@redhat.com> (raw)
In-Reply-To: <abe695ff4eb4a6bb5573dd11067f6614379e1542.camel@redhat.com>

On Thu, 2025-01-09 at 22:28 -0500, David Malcolm wrote:
> On Thu, 2025-01-09 at 21:15 -0500, Jason Merrill wrote:
> > On 1/9/25 7:00 PM, David Malcolm wrote:
> > > On Thu, 2025-01-09 at 14:21 -0500, Jason Merrill wrote:
> > > 
> > > Thanks for taking a look...
> > > 
> > > > > On 1/9/25 2:11 PM, David Malcolm wrote:
> > > > > 
> > > > > @@ -4743,7 +4769,38 @@ 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)
> > > > > +		      && TREE_CODE (TREE_PURPOSE (iter)) !=
> > > > > DEFERRED_PARSE)
> > > > > 
> > > > 
> > > > Why are you checking DEFERRED_PARSE?  That indicates a default
> > > > argument,
> > > > even if it isn't parsed yet.  For that case we should get the
> > > > error
> > > > in
> > > > convert_default_arg rather than pretend there's no default
> > > > argument.
> > > 
> > > I confess that the check for DEFERRED_PARSE was a rather mindless
> > > copy
> > > and paste by me from the "See if there are default arguments that
> > > can be
> > > used" logic earlier in the function.
> > > 
> > > I've removed it in the latest version of the patch.
> > >   
> > > > > +		    {
> > > > > +		      /* Found a default argument; skip this
> > > > > one when
> > > > > +			 counting minimum required.  */
> > > > > +		      at_least_p = true;
> > > > > +		      iter = TREE_CHAIN (iter);
> > > > > +		      continue;
> > > > 
> > > > We could break here, once you have a default arg the rest of
> > > > the
> > > > parms
> > > > need to have them as well.
> > > 
> > > Indeed; I've updated this in the latest version of the patch, so
> > > we break out as soon as we see an arg with a non-null
> > > TREE_PURPOSE.
> > > 
> > > > 
> > > > > +		    }
> > > > > +		  ++min_expected_num;
> > > > > +		  iter = TREE_CHAIN (iter);
> > > > > +		}
> > > > > +	      error_args_num (input_location, fndecl,
> > > > > +			      min_expected_num, actual_num,
> > > > > at_least_p);
> > > > > +	    }
> > > > >    	  return -1;
> > > > >    	}
> > > 
> > > Here's a v3 version of the patch, which is currently going
> > > through
> > > my tester.
> > > 
> > > OK for trunk if it passes bootstrap&regrtesting?
> > 
> > OK.
> 
> Thanks.  However, it turns out that I may have misspoke, and the v2
> patch might have been the correct approach: if we bail out on the
> first
> arg with a TREE_PURPOSE then in
> gcc/testsuite/g++.dg/cpp0x/variadic169.C
> 
>    1   │ // DR 2233
>    2   │ // { dg-do compile { target c++11 } }
>    3   │ 
>    4   │ template<typename ...T> void f(int n = 0, T ...t);
>    5   │ 
>    6   │ int main()
>    7   │ {
>    8   │   f<int>();         // { dg-error "too few arguments to
> function '\[^\n\r\]*'; expected at least 1, have 0" }
>    9   │ }
> 
> we instead emit the nonsensical "expected at least 0, have 0":
> 
> error: too few arguments to function ‘void f(int, T ...) [with T =
> {int}]’; expected at least 0, have 0
>     8 |   f<int>();                     // { dg-error "too few
> arguments to function '\[^\n\r\]*'; expected at least 1, have 0" }
>       |   ~~~~~~^~
> ../../src/gcc/testsuite/g++.dg/cpp0x/variadic169.C:4:30: note:
> declared
> here
>     4 | template<typename ...T> void f(int n = 0, T ...t);
>       |                              ^
> 
> whereas with the v2 patch we count the trailing arg after the default
> arg, and we emit "expected at least 1, have 0", which seems correct
> to
> me.
> 
> I'm testing a version of the patch that continues to iterate after a
> TREE_PURPOSE (as v2, but but dropping the check on DEFERRED_PARSE).
> 
> Thanks
> Dave

Hi Jason

Here's an updated version of the patch which drops the check on
DEFERRED_PARSE, but continues to iterate on TREE_PURPOSE, for dealing
with the case of explicit template args where default args are followed
by missing mandatory args (the code above mentions DR777, so I
referenced that).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

Are the C++ parts OK for trunk?

Thanks
Dave


Consider this case of a bad call to a callback function (perhaps
due to C23 changing the meaning of () in function decls):

struct p {
        int (*bar)();
};

void baz() {
    struct p q;
    q.bar(1);
}

Before this patch the C frontend emits:

t.c: In function 'baz':
t.c:7:5: error: too many arguments to function 'q.bar'
    7 |     q.bar(1);
      |     ^

and the C++ frontend emits:

t.c: In function 'void baz()':
t.c:7:10: error: too many arguments to function
    7 |     q.bar(1);
      |     ~~~~~^~~

neither of which give the user much help in terms of knowing what
was expected, and where the relevant declaration is.

With this patch the C frontend emits:

t.c: In function 'baz':
t.c:7:5: error: too many arguments to function 'q.bar'; expected 0, have 1
    7 |     q.bar(1);
      |     ^     ~
t.c:2:15: note: declared here
    2 |         int (*bar)();
      |               ^~~

(showing the expected vs actual counts, the pertinent field decl, and
underlining the first extraneous argument at the callsite)

and the C++ frontend emits:

t.c: In function 'void baz()':
t.c:7:10: error: too many arguments to function; expected 0, have 1
    7 |     q.bar(1);
      |     ~~~~~^~~

(showing the expected vs actual counts; the other data was not accessible
without a more invasive patch)

Similarly, the patch also updates the "too few arguments" case to also
show expected vs actual counts.  Doing so requires a tweak to the
wording to say "at least" for the case of variadic fns, and for C++ fns
with default args, where e.g. previously the C FE emitted:

s.c: In function 'test':
s.c:5:3: error: too few arguments to function 'callee'
    5 |   callee ();
      |   ^~~~~~
s.c:1:6: note: declared here
    1 | void callee (const char *, ...);
      |      ^~~~~~

with this patch it emits:

s.c: In function 'test':
s.c:5:3: error: too few arguments to function 'callee'; expected at least 1, have 0
    5 |   callee ();
      |   ^~~~~~
s.c:1:6: note: declared here
    1 | void callee (const char *, ...);
      |      ^~~~~~

gcc/c/ChangeLog:
	PR c/118112
	* c-typeck.cc (inform_declaration): Add "function_expr" param and
	use it for cases where we couldn't show the function decl to show
	field decls for callbacks.
	(build_function_call_vec): Add missing auto_diagnostic_group.
	Update for new param of inform_declaration.
	(convert_arguments): Likewise.  For the "too many arguments" case
	add the expected vs actual counts to the message, and if we have
	it, add the location_t of the first surplus param as a secondary
	location within the diagnostic.  For the "too few arguments" case,
	determine the minimum number of arguments required and add the
	expected vs actual counts to the message, tweaking it to "at least"
	for variadic functions.

gcc/cp/ChangeLog:
	PR c/118112
	* typeck.cc (error_args_num): Add params "expected_num",
	"actual_num", and "at_least_p".  Compute "too_many_p" from these
	rather than have it be a param.  Add expected vs actual counts to
	the messages and tweak them for the "at least" case.
	(convert_arguments): Update calls to error_args_num to pass in
	expected vs actual number, and the "at_least_p", determining this
	for the "too few" case.

gcc/testsuite/ChangeLog:
	PR c/118112
	* c-c++-common/too-few-arguments.c: New test.
	* c-c++-common/too-many-arguments.c: New test.
	* g++.dg/cpp0x/variadic169.C: Verify the reported expected vs
	actual argument counts.
	* g++.dg/modules/macloc-1_c.C: Update regexp for addition of param
	counts to error message.
	* g++.dg/modules/macloc-1_d.C: Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/c/c-typeck.cc                             |  77 +++++++++++--
 gcc/cp/typeck.cc                              | 104 ++++++++++++++----
 .../c-c++-common/too-few-arguments.c          |  38 +++++++
 .../c-c++-common/too-many-arguments.c         |  96 ++++++++++++++++
 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 +-
 7 files changed, 287 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/too-few-arguments.c
 create mode 100644 gcc/testsuite/c-c++-common/too-many-arguments.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index dbb688cabaa5..2a2083b847d8 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3737,14 +3737,30 @@ build_function_call (location_t loc, tree function, tree params)
   return ret;
 }
 
-/* Give a note about the location of the declaration of DECL.  */
+/* Give a note about the location of the declaration of DECL,
+   or, failing that, a pertinent declaration for FUNCTION_EXPR.  */
 
 static void
-inform_declaration (tree decl)
+inform_declaration (tree decl, tree function_expr)
 {
   if (decl && (TREE_CODE (decl) != FUNCTION_DECL
 	       || !DECL_IS_UNDECLARED_BUILTIN (decl)))
     inform (DECL_SOURCE_LOCATION (decl), "declared here");
+  else if (function_expr)
+    switch (TREE_CODE (function_expr))
+      {
+      default:
+	break;
+      case COMPONENT_REF:
+	/* Show the decl of the pertinent field (e.g. for callback
+	   fields in a struct.  */
+	{
+	  tree field_decl = TREE_OPERAND (function_expr, 1);
+	  if (location_t loc = DECL_SOURCE_LOCATION (field_decl))
+	    inform (loc, "declared here");
+	}
+	break;
+      }
 }
 
 /* C implementation of callback for use when checking param types.  */
@@ -3819,10 +3835,11 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 		  function);
       else if (DECL_P (function))
 	{
+	  auto_diagnostic_group d;
 	  error_at (loc,
 		    "called object %qD is not a function or function pointer",
 		    function);
-	  inform_declaration (function);
+	  inform_declaration (function, NULL_TREE);
 	}
       else
 	error_at (loc,
@@ -4276,25 +4293,37 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree fntype,
 
       if (type == void_type_node)
 	{
+	  auto_diagnostic_group d;
+	  int num_expected = parmnum;
+	  int num_actual = values->length ();
+	  gcc_rich_location rich_loc (loc);
+	  if (ploc != input_location)
+	    rich_loc.add_range (ploc);
 	  if (selector)
-	    error_at (loc, "too many arguments to method %qE", selector);
+	    error_at (&rich_loc,
+		      "too many arguments to method %qE; expected %i, have %i",
+		      selector, num_expected, num_actual);
 	  else
-	    error_at (loc, "too many arguments to function %qE", function);
-	  inform_declaration (fundecl);
+	    error_at (&rich_loc,
+		      "too many arguments to function %qE; expected %i, have %i",
+		      function, num_expected, num_actual);
+	  inform_declaration (fundecl, function);
 	  return error_args ? -1 : (int) parmnum;
 	}
 
       if (builtin_type == void_type_node)
 	{
+	  auto_diagnostic_group d;
 	  if (warning_at (loc, OPT_Wbuiltin_declaration_mismatch,
 			  "too many arguments to built-in function %qE "
 			  "expecting %d", function, parmnum))
-	    inform_declaration (fundecl);
+	    inform_declaration (fundecl, function);
 	  builtin_typetail = NULL_TREE;
 	}
 
       if (!typetail && parmnum == 0 && !TYPE_NO_NAMED_ARGS_STDARG_P (fntype))
 	{
+	  auto_diagnostic_group d;
 	  bool warned;
 	  if (selector)
 	    warned = warning_at (loc, OPT_Wdeprecated_non_prototype,
@@ -4307,7 +4336,7 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree fntype,
 				 " for function %qE declared without parameters",
 				 function);
 	  if (warned)
-	    inform_declaration (fundecl);
+	    inform_declaration (fundecl, function);
 	}
 
       if (selector && argnum > 2)
@@ -4437,8 +4466,33 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree fntype,
 
   if (typetail != NULL_TREE && TREE_VALUE (typetail) != void_type_node)
     {
-      error_at (loc, "too few arguments to function %qE", function);
-      inform_declaration (fundecl);
+      /* 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;
+	  ++min_expected_num;
+	  iter = TREE_CHAIN (iter);
+	}
+      auto_diagnostic_group d;
+      int actual_num = vec_safe_length (values);
+      error_at (loc,
+		at_least_p
+		? G_("too few arguments to function %qE; expected at least %i, have %i")
+		: G_("too few arguments to function %qE; expected %i, have %i"),
+		function, min_expected_num, actual_num);
+      inform_declaration (fundecl, function);
       return -1;
     }
 
@@ -4448,10 +4502,11 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree fntype,
       for (tree t = builtin_typetail; t; t = TREE_CHAIN (t))
 	++nargs;
 
+      auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wbuiltin_declaration_mismatch,
 		      "too few arguments to built-in function %qE "
 		      "expecting %u", function, nargs - 1))
-	inform_declaration (fundecl);
+	inform_declaration (fundecl, function);
     }
 
   return error_args ? -1 : (int) parmnum;
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 1f9a74166f48..1216c66a8274 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);
@@ -4533,11 +4533,19 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
 }
 \f
 /* 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;
@@ -4548,22 +4556,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");
     }
@@ -4572,12 +4586,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);
     }
 }
 
@@ -4607,9 +4628,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];
@@ -4621,7 +4644,10 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 	{
           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 i;
             }
           else
@@ -4743,7 +4769,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/c-c++-common/too-few-arguments.c b/gcc/testsuite/c-c++-common/too-few-arguments.c
new file mode 100644
index 000000000000..633dccfeaf24
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/too-few-arguments.c
@@ -0,0 +1,38 @@
+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)
+{
+  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" "" { 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" "" { 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" "" { target c } } */
+  /* { dg-error "too few arguments to function; expected 2, have 1" "" { target c++ } .-1 } */
+}
diff --git a/gcc/testsuite/c-c++-common/too-many-arguments.c b/gcc/testsuite/c-c++-common/too-many-arguments.c
new file mode 100644
index 000000000000..7f9f8d4870bf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/too-many-arguments.c
@@ -0,0 +1,96 @@
+/* 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" "" { 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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_a ();
+             ^~~~
+     { dg-end-multiline-output "" { target c } } */
+
+  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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_b (void);
+             ^~~~
+     { dg-end-multiline-output "" { target c } } */
+
+  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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_c (int);
+             ^~~~
+     { 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" "" { 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" "" { 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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_a)();
+          ^~~~~~~~~~
+     { dg-end-multiline-output "" { target c } } */
+  
+  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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_b)(void);
+          ^~~~~~~~~~
+     { 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" "" { 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 "" { target c } } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_c)(int);
+          ^~~~~~~~~~
+     { dg-end-multiline-output "" { target c } } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic169.C b/gcc/testsuite/g++.dg/cpp0x/variadic169.C
index 6858973cd2eb..460bb3b8a193 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 5865a34687e7..3f980c213814 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 282a31c4a2d1..56c001fc3f83 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" }
-- 
2.26.3






  reply	other threads:[~2025-01-10 18:47 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-19 23:40 [PATCH] " David Malcolm
2025-01-02 19:01 ` [PATCH] c/c++: UX improvements to 'too {few, many} " Joseph Myers
2025-01-07 17:20 ` Ping re C++ parts of: [PATCH] c/c++: UX improvements to 'too {few,many} " David Malcolm
2025-01-07 20:08 ` [PATCH] c/c++: UX improvements to 'too {few, many} " Marek Polacek
2025-01-07 23:52   ` David Malcolm
2025-01-08 14:55     ` Marek Polacek
2025-01-09 19:11       ` [PATCH] c/c++: UX improvements to 'too {few,many} arguments' errors (v2) [PR118112] David Malcolm
2025-01-09 19:21         ` Jason Merrill
2025-01-10  0:00           ` [PATCH] c/c++: UX improvements to 'too {few,many} arguments' errors (v3) [PR118112] David Malcolm
2025-01-10  2:15             ` Jason Merrill
2025-01-10  3:28               ` David Malcolm
2025-01-10 18:47                 ` David Malcolm [this message]
2025-01-11 21:32                   ` [PATCH v4] c/c++: UX improvements to 'too {few,many} arguments' errors [PR118112] Jason Merrill
2025-01-12 19:03                     ` [pushed: r15-6838] c: UX improvements to 'too {few,many} arguments' errors (v5) [PR118112] David Malcolm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f34fdf57159821af93c7da1643edfa4796a77ad4.camel@redhat.com \
    --to=dmalcolm@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=polacek@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).