diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 6e40f7edf02ae95a90594cd71aff6da02cfe9a6e..cd9290160d7a391185298dd6cf7e836570e2cf68 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/testsuite/gcc.dg/too-few-arguments.c b/gcc/testsuite/gcc.dg/too-few-arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..4406e021e0c321dee920a40fffa2544e803a32ce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/too-few-arguments.c
@@ -0,0 +1,28 @@
+extern void fn_a (void);
+extern void fn_b (int); /* { dg-message "declared here" } */
+extern void fn_c (int, int); /* { dg-message "declared here" } */
+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" } */
+  fn_f ();  /* { dg-error "too few arguments to function '\[^\n\r\]*'; expected at least 1, have 0" } */
+}
+
+struct foo
+{
+  void (*callback_a) (void);
+  void (*callback_b) (int); /* { dg-message "declared here" } */
+  void (*callback_c) (int, int); /* { dg-message "declared here" } */
+};
+
+void test_callback (struct foo *f)
+{
+  f->callback_a ();
+  
+  f->callback_b (); /* { dg-error "too few arguments to function 'f->callback_b'; expected 1, have 0" } */
+
+  f->callback_c (42); /* { dg-error "too few arguments to function 'f->callback_c'; expected 2, have 1" } */
+}
diff --git a/gcc/testsuite/gcc.dg/too-many-arguments.c b/gcc/testsuite/gcc.dg/too-many-arguments.c
new file mode 100644
index 0000000000000000000000000000000000000000..5bbd4a306c3b21e6229e527fb80540fb0042e66c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/too-many-arguments.c
@@ -0,0 +1,83 @@
+/* Ensure that we get an error on the call to fn_a with an
+   int arg below.  */
+/* { dg-additional-options "-std=c23" } */
+
+/* Verify that the first excess param is underlined.  */
+/* { dg-additional-options "-fdiagnostics-show-caret" } */
+
+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" } */
+
+void test_known_fn (void)
+{
+  fn_a (42); /* { dg-error "too many arguments to function 'fn_a'; expected 0, have 1" } */
+  /* { dg-begin-multiline-output "" }
+   fn_a (42);
+   ^~~~  ~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_a ();
+             ^~~~
+     { dg-end-multiline-output "" } */
+
+  fn_b (1776); /* { dg-error "too many arguments to function 'fn_b'; expected 0, have 1" } */
+  /* { dg-begin-multiline-output "" }
+   fn_b (1776);
+   ^~~~  ~~~~
+   { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_b (void);
+             ^~~~
+     { dg-end-multiline-output "" } */
+
+  fn_c (1066, 1649);  /* { dg-error "too many arguments to function 'fn_c'; expected 1, have 2" } */
+  /* { dg-begin-multiline-output "" }
+   fn_c (1066, 1649);
+   ^~~~        ~~~~
+   { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ extern void fn_c (int);
+             ^~~~
+     { dg-end-multiline-output "" } */
+}
+
+struct foo
+{
+  void (*callback_a)(); /* { dg-message "declared here" } */
+  void (*callback_b)(void); /* { dg-message "declared here" } */
+  void (*callback_c)(int); /* { dg-message "declared here" } */
+};
+
+void test_callback (struct foo *f)
+{
+  f->callback_a (42); /* { dg-error "too many arguments to function 'f->callback_a'; expected 0, have 1" } */
+  /* { dg-begin-multiline-output "" }
+   f->callback_a (42);
+   ^              ~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_a)();
+          ^~~~~~~~~~
+     { dg-end-multiline-output "" } */
+  
+  f->callback_b (1776); /* { dg-error "too many arguments to function 'f->callback_b'; expected 0, have 1" } */
+  /* { dg-begin-multiline-output "" }
+   f->callback_b (1776);
+   ^              ~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_b)(void);
+          ^~~~~~~~~~
+     { dg-end-multiline-output "" } */
+
+  f->callback_c (1066, 1649); /* { dg-error "too many arguments to function 'f->callback_c'; expected 1, have 2" } */
+  /* { dg-begin-multiline-output "" }
+   f->callback_c (1066, 1649);
+   ^                    ~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   void (*callback_c)(int);
+          ^~~~~~~~~~
+     { dg-end-multiline-output "" } */
+}