On Tue, 2025-01-07 at 15:08 -0500, Marek Polacek wrote:
> On Thu, Dec 19, 2024 at 06:40:19PM -0500, David Malcolm wrote:
> > 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 *, ...);
> >       |      ^~~~~~
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > OK for trunk?
> > 
> > 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 <dmalc...@redhat.com>
> 
> [...]
> 
> > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > index 964e549a6122..2966931ca8c1 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,16 @@ 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.  */
> 
> Should probably mention ACTUAL_NUM in the comment too.

Done; I've updated that comment to:

/* Subroutine of convert_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 +4553,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);
> >     }
> 
> Looks like the METHOD_TYPE block is not exercised in our testsuite
> :(.

I'll take a look at adding test coverage for that, I guess.  I know
next to nothing about Objective C++ though :(

> 
> >        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 +4583,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,6 +4625,10 @@ convert_arguments (tree typelist, vec<tree,
> > va_gc> **values, tree fndecl,
> >    /* Argument passing is always copy-initialization.  */
> >    flags |= LOOKUP_ONLYCONVERTING;
> >  
> > +  /* Preserve actual number of arguments passed (without counting
> > default
> > +     args), in case we need to complain about too many/few.  */
> > +  int actual_num = vec_safe_length (*values);
> 
> If we're computing the length here, we don't need to compute it
> again...
> 
> > +
> >    for (i = 0, typetail = typelist;
> >         i < vec_safe_length (*values);
> 
> ...here.

Will fix.


> 
> >         i++)
> > @@ -4621,7 +4643,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.  */
> > +         int expected_num = i;
> 
> Why not just pass i?  error_args_num doesn't modify its args.

I wanted to document the meaning of the argument by giving it a name,
since "i" seems rather nondescript to me.  But I can drop that if it
seems weird.

> 
> > +         error_args_num (input_location, fndecl,
> > expected_num, actual_num,
> > +                         false);
> 
> Nit, but I'd like to have it as /*at_least_p=*/false.

Will fix.

> 
> >                return i;
> >              }
> >            else
> > @@ -4743,7 +4768,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)
> > +               {
> > +                 /* Found a default argument; skip this one
> > when
> > +                    counting minimum required.  */
> > +                 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;
> >     }
> >      }
> 
> I haven't found any issues otherwise.

Thanks; I'm working on an updated patch.

Dave

Reply via email to