Thanks Jason for notes on v4 of the patch.

The C parts of the patch are relatively simple, approved by Joseph, and
IMHO will be very helpful to users for GCC 15's C23-by-default
migration.

For the C++ parts, I don't feel 100% comfortable that I understand all
the issues Jason is pointing out, and it's not relevant to C23, so
rather than try to rush things this late in stage 3, I've split up the
patch to do just the C material for GCC 15, moving the test cases from
c-c++-common to gcc.dg.  I hope to revisit the C++ material in GCC 16;
we could move the test cases back to c-c++-common then.

Here's what I've pushed to trunk for the C frontend (as
r15-6838-ga236f706172133, v5 of the patch), after successfully bootstrap
& regrtesting on x86_64-pc-linux-gnu:


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);
      |     ^

which doesn't 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)

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 where
previously the C FE emitted e.g.:

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/testsuite/ChangeLog:
        PR c/118112
        * gcc.dg/too-few-arguments.c: New test.
        * gcc.dg/too-many-arguments.c: New test.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/c/c-typeck.cc                         | 77 ++++++++++++++++++---
 gcc/testsuite/gcc.dg/too-few-arguments.c  | 28 ++++++++
 gcc/testsuite/gcc.dg/too-many-arguments.c | 83 +++++++++++++++++++++++
 3 files changed, 177 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/too-few-arguments.c
 create mode 100644 gcc/testsuite/gcc.dg/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/testsuite/gcc.dg/too-few-arguments.c 
b/gcc/testsuite/gcc.dg/too-few-arguments.c
new file mode 100644
index 000000000000..4406e021e0c3
--- /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 000000000000..5bbd4a306c3b
--- /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 "" } */
+}
-- 
2.26.3

Reply via email to