[v4] c/c++: UX improvements to 'too {few,many} arguments' errors [PR118112]

Message ID f34fdf57159821af93c7da1643edfa4796a77ad4.camel@redhat.com
State New
Headers
Series [v4] c/c++: UX improvements to 'too {few,many} arguments' errors [PR118112] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

David Malcolm Jan. 10, 2025, 6:47 p.m. UTC
  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
  

Comments

Jason Merrill Jan. 11, 2025, 9:32 p.m. UTC | #1
On 1/10/25 1:47 PM, David Malcolm wrote:
> 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":

Ah, right.

>> 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.

In that case I think the actual minimum is 2, we can't use the default 
argument.

The corresponding code in add_function_candidate uses 
remaining_arguments; it would be good to harmonize them.

>> 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,
>   }
>   
>   /* 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" }
  

Patch

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,
 }
 
 /* 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" }