On Tue, 2026-01-13 at 15:37 +0800, Jason Merrill wrote:
> On 1/13/26 3:18 AM, David Malcolm wrote:
> > > On Mon, 2026-01-12 at 17:01 +0800, Jason Merrill wrote:
> > > > On 1/11/26 6:54 AM, David Malcolm wrote:
> > > > Thanks Patrick and Jason. Here's an updated version,
> > > > incorporating
> > > > your feedback.
> > > >
> > > > Changed in v2:
> > > > * be consistent about using C-style comments
> > > > * use same_type_p rather than pointer comparison
> > > > * move class decl_mismatch_context out of function
> > > > * use auto_vec rather than std::vector
> > > > * handle void_type in param list by setting m_variadic, rather
> > > > than adding it to vec
> >
> > > This is backwards: Ending with void_list_node indicates non-
> > > variadic.
> >
> > Oops; sorry. That said, the patch didn't make use of m_variadic,
> > so
> > here's an updated version that drops it, and simply skips any
> > trailing
> > void_type in the list.
>
> So we don't give an explanatory note for bad-fndef-7.C, and if we
> change
> one of the parameter types we get a note about that difference while
> still not mentioning the ... difference?
Good point.
For reference, with the patches, we emit this for bad-fndef-7.C as
written:
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C:9:6: error: no
declaration matches ‘void foo::test(int, int, int)’
9 | void foo::test (int i, int j, int k)
| ^~~
• there is 1 candidate
• candidate is: ‘void foo::test(int, int, int, ...)’
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C:4:8:
4 | void test (int i, int j, int k, ...);
| ^~~~
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C:1:7: note:
‘class foo’ defined here
1 | class foo
| ^~~
It might be worth get_differences treating the "has variadic args" vs
"doesn't have variadic args" case as another kind of difference (versus
the "parm IDX has a different type" parm_difference), and only report
it for the "single difference" case, which might lead to a wording like
this:
• candidate accepts variadic arguments (`...’):
4 | void test (int i, int j, int k, ...);
| ^~~~
• ...whereas declaration does not:
9 | void foo::test (int i, int j, int k)
| ^~~
but I don't love that wording, and I'm not sure it's adds enough
readability for the extra verbosity. Also, it increases the
complexity of the patch, and I'd prefer to land the patch as-is.
Alternatively, I'll experiment with variadic vs non-variadic being a
"difference" in terms of the *number* of differences between a
candidate and the decl (and thus not printing if they are too similar),
but without a message. That would fix the case of changing one of the
param types like this, for which currently the patch emits:
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C:9:6: error: no
declaration matches ‘void foo::test(int, int, int)’
9 | void foo::test (int i, int j, int k)
| ^~~
• there is 1 candidate
• candidate is: ‘void foo::test(int, int, const char*, ...)’
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C:4:8:
4 | void test (int i, int j, const char *k, ...);
| ^~~~
• parameter 3 of candidate has type ‘const char*’...
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C:4:40:
4 | void test (int i, int j, const char *k, ...);
| ~~~~~~~~~~~~^
• ...which does not match type ‘int’
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C:9:35:
9 | void foo::test (int i, int j, int k)
| ~~~~^
../../src/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C:1:7: note:
‘class foo’ defined here
1 | class foo // { dg-message "'class foo' defined here" }
| ^~~
for which the notes about param 3 seem overkill.
>
> That seems suboptimal, but the patch is OK as is if you don't want to
> pursue improving that now.
Thanks
Dave
>
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > OK for trunk?
> >
> > Thanks
> > Dave
> >
> >
> > Changed in v3:
> > * drop m_variadic in favor of simply skipping trailing void_type in
> > param list if present
> >
> > Changed in v2:
> > * be consistent about using C-style comments
> > * use same_type_p rather than pointer comparison
> > * move class decl_mismatch_context out of function
> > * use auto_vec rather than std::vector
> > * handle void_type in param list by setting m_variadic, rather
> > than adding it to vec
> > * add test cases of variadic vs non-variadic
> >
> > Blurb from v1:
> >
> > This patch improves the UX for various cases of "no declaration
> > matches"
> > where print_candidates encounters a close match.
> >
> > For example, consider the const vs non-const here:
> >
> > class foo
> > {
> > public:
> > void test (int i, int j, void *ptr, int k);
> > };
> >
> > // Wrong "const"-ness of param 3.
> > void foo::test (int i, int j, const void *ptr, int k)
> > {
> > }
> >
> > where we emit (with indentation provided by the prior patch):
> >
> > test.cc:8:6: error: no declaration matches ‘void foo::test(int,
> > int, const void*, int)’
> > 8 | void foo::test (int i, int j, const void *ptr, int k)
> > | ^~~
> > • there is 1 candidate
> > • candidate is: ‘void foo::test(int, int, void*, int)’
> > test.cc:4:8:
> > 4 | void test (int i, int j, void *ptr, int k);
> > | ^~~~
> > test.cc:1:7: note: ‘class foo’ defined here
> > 1 | class foo
> > | ^~~
> >
> > which requires the user to look through the pairs of parameters
> > and try to find the mismatch by eye.
> >
> > This patch adds notes identifying that parameter 3 has the
> > mismatch, and
> > what the mismatch is, using a pair of colors to highlight and
> > contrast
> > the type mismatch.
> >
> > test.cc:8:6: error: no declaration matches ‘void foo::test(int,
> > int, const void*, int)’
> > 8 | void foo::test (int i, int j, const void *ptr, int k)
> > | ^~~
> > • there is 1 candidate
> > • candidate is: ‘void foo::test(int, int, void*, int)’
> > test.cc:4:8:
> > 4 | void test (int i, int j, void *ptr, int k);
> > | ^~~~
> > • parameter 3 of candidate has type ‘void*’...
> > test.cc:4:34:
> > 4 | void test (int i, int j, void *ptr, int k);
> > | ~~~~~~^~~
> > • ...which does not match type ‘const void*’
> > test.cc:8:43:
> > 8 | void foo::test (int i, int j, const void *ptr, int
> > k)
> > | ~~~~~~~~~~~~^~~
> > test.cc:1:7: note: ‘class foo’ defined here
> > 1 | class foo
> > | ^~~
> >
> > This also works for the "this" case, improving the UX for messing
> > up
> > const vs non-const between decls and defns of member functions; see
> > bad-fndef-2.C for an example.
> >
> > For screenshots showing the colorization effect, see slides 9-12 of
> > my
> > Cauldron talk:
> > https://gcc.gnu.org/wiki/cauldron2025#What.27s_new_with_diagnostics_in_GCC_16
> > ("Hierarchical diagnostics (not yet in trunk)").
> >
> > gcc/cp/ChangeLog:
> > * call.cc (get_fndecl_argument_location): Use
> > DECL_SOURCE_LOCATION
> > for "this".
> > * cp-tree.h (class candidate_context): New.
> > (print_candidates): Add optional candidate_context param.
> > * decl2.cc: Include "gcc-rich-location.h" and
> > "tree-pretty-print-markup.h".
> > (struct fndecl_signature): New.
> > (class parm_rich_location): New.
> > (class fndecl_comparison): New.
> > (class decl_mismatch_context): New.
> > (check_classfn): For the "no declaration matches" case,
> > pass an
> > instance of a custom candidate_context subclass to
> > print_candidates, using fndecl_comparison to report on
> > close
> > matches.
> > * pt.cc (print_candidates): Add optional candidate_context
> > param.
> > Use it if provided to potentially emit per-candidate
> > notes.
> >
> > gcc/testsuite/ChangeLog:
> > * g++.dg/diagnostic/bad-fndef-1.C: Add directives to
> > expect
> > "void *" vs "const void *" notes about parameter 3 of the
> > close
> > candidate.
> > * g++.dg/diagnostic/bad-fndef-2.C: New test.
> > * g++.dg/diagnostic/bad-fndef-3.C: New test.
> > * g++.dg/diagnostic/bad-fndef-4.C: New test.
> > * g++.dg/diagnostic/bad-fndef-5.C: New test.
> > * g++.dg/diagnostic/bad-fndef-6.C: New test.
> > * g++.dg/diagnostic/bad-fndef-7.C: New test.
> > * g++.dg/diagnostic/bad-fndef-8.C: New test.
> > * g++.dg/diagnostic/bad-fndef-9.C: New test.
> >
> > Signed-off-by: David Malcolm <[email protected]>
> >
> > FIXME: fixups to decl2.cc for variadic
> > ---
> > gcc/cp/call.cc | 3 +
> > gcc/cp/cp-tree.h | 15 +-
> > gcc/cp/decl2.cc | 201
> > +++++++++++++++++-
> > gcc/cp/pt.cc | 19 +-
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-1.C | 2 +
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-2.C | 15 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-3.C | 16 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-4.C | 38 ++++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-5.C | 15 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-6.C | 17 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C | 14 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C | 14 ++
> > gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C | 14 ++
> > 13 files changed, 377 insertions(+), 6 deletions(-)
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-2.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-3.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-4.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-5.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-6.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C
> > create mode 100644 gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C
> >
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index fa8281f01ba..66c2dbe90d1 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -8593,6 +8593,9 @@ get_fndecl_argument_location (tree fndecl,
> > int argnum)
> > if (DECL_ARTIFICIAL (fndecl))
> > return DECL_SOURCE_LOCATION (fndecl);
> >
> > + if (argnum == -1)
> > + return DECL_SOURCE_LOCATION (fndecl);
> > +
> > int i;
> > tree param;
> >
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 01843f7fc92..931d2e5ddfd 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7942,7 +7942,20 @@ extern tree
> > most_specialized_instantiation (tree);
> > extern tree most_specialized_partial_spec (tree,
> > tsubst_flags_t, bool = false);
> > extern tree most_constrained_function (tree);
> > extern void inform_num_candidates (location_t, int);
> > -extern void print_candidates (location_t,
> > tree);
> > +
> > +/* Abstract base class for optionally providing extra diagnostic
> > note(s)
> > + about a candidate in calls to print_candidates. */
> > +
> > +class candidate_context
> > +{
> > +public:
> > + virtual ~candidate_context () {}
> > + virtual void emit_any_notes_for_candidate (tree cand_fndecl) =
> > 0;
> > +};
> > +
> > +extern void print_candidates (location_t, tree,
> > + candidate_context
> > * = nullptr);
> > +
> > extern void instantiate_pending_templates (int);
> > extern tree tsubst_default_argument (tree, int, tree,
> > tree,
> > tsubst_flags_t);
> > diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> > index 5eb2ed42732..fe34784840b 100644
> > --- a/gcc/cp/decl2.cc
> > +++ b/gcc/cp/decl2.cc
> > @@ -53,6 +53,8 @@ along with GCC; see the file COPYING3. If not
> > see
> > #include "tree-inline.h"
> > #include "escaped_string.h"
> > #include "contracts.h"
> > +#include "gcc-rich-location.h"
> > +#include "tree-pretty-print-markup.h"
> >
> > /* Id for dumping the raw trees. */
> > int raw_dump_id;
> > @@ -770,6 +772,200 @@ check_member_template (tree tmpl)
> > error ("template declaration of %q#D", decl);
> > }
> >
> > +/* A struct for conveniently working with the parameter types of a
> > + fndecl. */
> > +
> > +struct fndecl_signature
> > +{
> > + fndecl_signature (tree fndecl)
> > + : m_fndecl (fndecl),
> > + m_num_artificial_parms (num_artificial_parms_for (fndecl))
> > + {
> > + function_args_iterator iter;
> > + tree argtype;
> > + FOREACH_FUNCTION_ARGS (TREE_TYPE (fndecl), argtype, iter)
> > + {
> > + /* Ignore any trailing "void" type. */
> > + if (VOID_TYPE_P (argtype))
> > + {
> > + gcc_assert (!TREE_CHAIN (iter.next));
> > + break;
> > + }
> > + m_parm_types.safe_push (argtype);
> > + }
> > + gcc_assert (m_parm_types.length () >=
> > (size_t)m_num_artificial_parms);
> > + m_num_user_parms = m_parm_types.length () -
> > m_num_artificial_parms;
> > + }
> > +
> > + /* Convert from index within DECL_ARGUMENTS to 0-based user-
> > written
> > + parameter, or -1 for "this". */
> > + int
> > + get_argno_from_idx (size_t idx) const
> > + {
> > + return static_cast<int> (idx) - m_num_artificial_parms;
> > + }
> > +
> > + location_t
> > + get_argno_location (int argno) const
> > + {
> > + return get_fndecl_argument_location (m_fndecl, argno);
> > + }
> > +
> > + tree m_fndecl;
> > + int m_num_artificial_parms;
> > + auto_vec<tree> m_parm_types;
> > + size_t m_num_user_parms;
> > +};
> > +
> > +/* A rich_location subclass that highlights a given argno
> > + within FNDECL_SIG using HIGHLIGHT_COLOR in the quoted source.
> > */
> > +
> > +class parm_rich_location : public gcc_rich_location
> > +{
> > +public:
> > + parm_rich_location (const fndecl_signature &fndecl_sig,
> > + int argno,
> > + const char *highlight_color)
> > + : gcc_rich_location (fndecl_sig.get_argno_location (argno),
> > + nullptr,
> > + highlight_color)
> > + {
> > + }
> > +};
> > +
> > +/* A class for comparing a pair of fndecls and emitting diagnostic
> > notes
> > + about the differences between them, if they are sufficiently
> > close. */
> > +
> > +class fndecl_comparison
> > +{
> > +public:
> > + fndecl_comparison (tree decl_a,
> > + tree decl_b)
> > + : m_decl_a (decl_a),
> > + m_decl_b (decl_b)
> > + {
> > + gcc_assert (TREE_CODE (decl_a) == FUNCTION_DECL);
> > + gcc_assert (TREE_CODE (decl_b) == FUNCTION_DECL);
> > +
> > + }
> > +
> > + /* Attempt to emit diagnostic notes describing the differences
> > between
> > + the fndecls, when they are a sufficiently close match. */
> > + void
> > + maybe_emit_notes_for_close_match () const
> > + {
> > + auto_vec<parm_difference> differences = get_differences ();
> > + if (differences.length () == 1)
> > + {
> > + auto_diagnostic_nesting_level sentinel;
> > + print_parm_type_difference (differences[0]);
> > + }
> > + }
> > +
> > +private:
> > + struct parm_difference
> > + {
> > + size_t m_parm_idx_a;
> > + size_t m_parm_idx_b;
> > + };
> > +
> > + /* If we have a close match, return the differences between the
> > two
> > + fndecls. Otherwise return an empty vector. */
> > + auto_vec<parm_difference>
> > + get_differences () const
> > + {
> > + auto_vec<parm_difference> differences;
> > +
> > + /* For now, just handle the case where the "user parm" count
> > is
> > + equal. */
> > + if (m_decl_a.m_num_user_parms != m_decl_b.m_num_user_parms)
> > + return differences;
> > +
> > + /* Ideally we'd check the edit distance, and thus report on
> > close
> > + matches which are missing a param, have transposed params,
> > etc.
> > + For now, just iterate through the params, finding
> > differences
> > + elementwise. */
> > +
> > + /* Find differences in artificial params, if they are
> > comparable.
> > + This should find e.g. const vs non-const differences in
> > "this". */
> > + if (m_decl_a.m_num_artificial_parms ==
> > m_decl_b.m_num_artificial_parms)
> > + for (int parm_idx = 0;
> > + parm_idx < m_decl_a.m_num_artificial_parms;
> > + ++parm_idx)
> > + compare (parm_idx, parm_idx, differences);
> > +
> > + /* Find differences in user-provided params. */
> > + for (size_t user_parm_idx = 0;
> > + user_parm_idx < m_decl_a.m_num_user_parms;
> > + ++user_parm_idx)
> > + {
> > + const size_t idx_a = m_decl_a.m_num_artificial_parms +
> > user_parm_idx;
> > + const size_t idx_b = m_decl_b.m_num_artificial_parms +
> > user_parm_idx;
> > + compare (idx_a, idx_b, differences);
> > + }
> > +
> > + return differences;
> > + }
> > +
> > + void
> > + compare (size_t idx_a, size_t idx_b,
> > + auto_vec<parm_difference> &differences) const
> > + {
> > + if (!same_type_p (m_decl_a.m_parm_types[idx_a],
> > + m_decl_b.m_parm_types[idx_b]))
> > + differences.safe_push (parm_difference {idx_a, idx_b});
> > + }
> > +
> > + void
> > + print_parm_type_difference (const parm_difference &diff) const
> > + {
> > + const char * const highlight_a = "highlight-a";
> > + pp_markup::element_quoted_type type_of_parm_a
> > + (m_decl_a.m_parm_types[diff.m_parm_idx_a],
> > + highlight_a);
> > + const int argno_a = m_decl_a.get_argno_from_idx
> > (diff.m_parm_idx_a);
> > + parm_rich_location rich_loc_a (m_decl_a, argno_a,
> > highlight_a);
> > + inform (&rich_loc_a,
> > + "parameter %P of candidate has type %e...",
> > + argno_a, &type_of_parm_a);
> > +
> > + const char * const highlight_b = "highlight-b";
> > + pp_markup::element_quoted_type type_of_parm_b
> > + (m_decl_b.m_parm_types[diff.m_parm_idx_b],
> > + highlight_b);
> > + const int argno_b = m_decl_b.get_argno_from_idx
> > (diff.m_parm_idx_b);
> > + parm_rich_location rich_loc_b (m_decl_b, argno_b,
> > highlight_b);
> > + inform (&rich_loc_b,
> > + "...which does not match type %e",
> > + &type_of_parm_b);
> > + }
> > +
> > + fndecl_signature m_decl_a;
> > + fndecl_signature m_decl_b;
> > +};
> > +
> > +class decl_mismatch_context : public candidate_context
> > +{
> > +public:
> > + decl_mismatch_context (tree function)
> > + : m_function (function)
> > + {
> > + gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
> > + }
> > +
> > + void
> > + emit_any_notes_for_candidate (tree cand) final override
> > + {
> > + if (TREE_CODE (cand) == FUNCTION_DECL)
> > + {
> > + fndecl_comparison diff (cand, m_function);
> > + diff.maybe_emit_notes_for_close_match ();
> > + }
> > + }
> > +private:
> > + tree m_function;
> > +};
> > +
> > /* Sanity check: report error if this function FUNCTION is not
> > really a member of the class (CTYPE) it is supposed to belong
> > to.
> > TEMPLATE_PARMS is used to specify the template parameters of a
> > member
> > @@ -917,7 +1113,10 @@ check_classfn (tree ctype, tree function,
> > tree template_parms)
> > error_at (DECL_SOURCE_LOCATION (function),
> > "no declaration matches %q#D", function);
> > if (fns)
> > - print_candidates (DECL_SOURCE_LOCATION (function),
> > fns);
> > + {
> > + decl_mismatch_context ctxt (function);
> > + print_candidates (DECL_SOURCE_LOCATION (function),
> > fns, &ctxt);
> > + }
> > else if (DECL_CONV_FN_P (function))
> > inform (DECL_SOURCE_LOCATION (function),
> > "no conversion operators declared");
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 9bc15527ea4..0b7cc36ef33 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -2065,10 +2065,15 @@ inform_num_candidates (location_t loc, int
> > num_candidates)
> > }
> >
> > /* Print the list of candidate FNS in an error message. FNS can
> > also
> > - be a TREE_LIST of non-functions in the case of an ambiguous
> > lookup. */
> > + be a TREE_LIST of non-functions in the case of an ambiguous
> > lookup.
> > +
> > + If CAND_CTXT is non-null, use it for each candidate to allow
> > for
> > + additional per-candidate notes. */
> >
> > void
> > -print_candidates (location_t error_loc, tree fns)
> > +print_candidates (location_t error_loc,
> > + tree fns,
> > + candidate_context *cand_ctxt)
> > {
> > auto_vec<tree> candidates;
> > flatten_candidates (fns, candidates);
> > @@ -2083,13 +2088,19 @@ print_candidates (location_t error_loc,
> > tree fns)
> > {
> > tree cand = candidates[0];
> > inform (DECL_SOURCE_LOCATION (cand), "candidate is: %#qD",
> > cand);
> > + if (cand_ctxt)
> > + cand_ctxt->emit_any_notes_for_candidate (cand);
> > }
> > else
> > {
> > int idx = 0;
> > for (tree cand : candidates)
> > - inform (DECL_SOURCE_LOCATION (cand), "candidate %i: %#qD",
> > - ++idx, cand);
> > + {
> > + inform (DECL_SOURCE_LOCATION (cand), "candidate %i:
> > %#qD",
> > + ++idx, cand);
> > + if (cand_ctxt)
> > + cand_ctxt->emit_any_notes_for_candidate (cand);
> > + }
> > }
> > }
> >
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-1.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-1.C
> > index 1156072b11f..60d6c5baa0b 100644
> > --- a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-1.C
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-1.C
> > @@ -13,3 +13,5 @@ void foo::test (int i, int j, const void *ptr,
> > int k) // { dg-line defn }
> > // { dg-error "6: no declaration matches" "error" { target *-*-*
> > } defn }
> > // { dg-message "8: candidate 1: " "candidate 1" { target *-*-* }
> > other_decl }
> > // { dg-message "8: candidate 2: " "candidate 2" { target *-*-* }
> > close_decl }
> > +// { dg-message "34: parameter 3 of candidate has type 'void\\*'"
> > "param of decl" { target *-*-* } close_decl }
> > +// { dg-message "43: \\.\\.\\.which does not match type 'const
> > void\\*'" "param of defn" { target *-*-* } defn }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-2.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-2.C
> > new file mode 100644
> > index 00000000000..068b392928d
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-2.C
> > @@ -0,0 +1,15 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, int k); // { dg-line decl }
> > +};
> > +
> > +// Wrong "const"-ness of this:
> > +void foo::test (int i, int j, int k) const // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > +// { dg-message "8: parameter 'this' of candidate has type
> > 'foo\\*'" "this of decl" { target *-*-* } decl }
> > +// { dg-message "6: \\.\\.\\.which does not match type 'const
> > foo\\*'" "this of defn" { target *-*-* } defn }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-3.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-3.C
> > new file mode 100644
> > index 00000000000..452334dc572
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-3.C
> > @@ -0,0 +1,16 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, int k) const; // { dg-line decl }
> > +};
> > +
> > +// Wrong "const"-ness of "this":
> > +
> > +void foo::test (int i, int j, int k) // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > +// { dg-message "8: parameter 'this' of candidate has type 'const
> > foo\\*'" "this of decl" { target *-*-* } decl }
> > +// { dg-message "6: \\.\\.\\.which does not match type 'foo\\*'"
> > "this of defn" { target *-*-* } defn }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-4.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-4.C
> > new file mode 100644
> > index 00000000000..afbcb76cb28
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-4.C
> > @@ -0,0 +1,38 @@
> > +// { dg-additional-options "-fdiagnostics-show-caret" }
> > +
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, void *ptr, int k); // { dg-line decl }
> > +};
> > +
> > +// Wrong "const"-ness of a param (param 3).
> > +void foo::test (int i, int j, const void *ptr, int k) // { dg-line
> > defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +/* { dg-begin-multiline-output "" }
> > + void foo::test (int i, int j, const void *ptr, int k)
> > + ^~~
> > + { dg-end-multiline-output "" } */
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > +/* { dg-begin-multiline-output "" }
> > + void test (int i, int j, void *ptr, int k);
> > + ^~~~
> > + { dg-end-multiline-output "" } */
> > +// { dg-message "34: parameter 3 of candidate has type 'void\\*'"
> > "param of decl" { target *-*-* } decl }
> > +/* { dg-begin-multiline-output "" }
> > + void test (int i, int j, void *ptr, int k);
> > + ~~~~~~^~~
> > + { dg-end-multiline-output "" } */
> > +// { dg-message "43: \\.\\.\\.which does not match type 'const
> > void\\*'" "param of defn" { target *-*-* } defn }
> > +/* { dg-begin-multiline-output "" }
> > + void foo::test (int i, int j, const void *ptr, int k)
> > + ~~~~~~~~~~~~^~~
> > + { dg-end-multiline-output "" } */
> > +
> > +/* { dg-begin-multiline-output "" }
> > + class foo
> > + ^~~
> > + { dg-end-multiline-output "" } */
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-5.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-5.C
> > new file mode 100644
> > index 00000000000..498395f4e15
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-5.C
> > @@ -0,0 +1,15 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + static void test (int i, int j, void *ptr, int k); // { dg-line
> > decl }
> > +};
> > +
> > +// Wrong "const"-ness of a param, for static member fn (param 3).
> > +void foo::test (int i, int j, const void *ptr, int k) // { dg-line
> > defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "15: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > +// { dg-message "41: parameter 3 of candidate has type 'void\\*'"
> > "param of decl" { target *-*-* } decl }
> > +// { dg-message "43: \\.\\.\\.which does not match type 'const
> > void\\*'" "param of defn" { target *-*-* } defn }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-6.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-6.C
> > new file mode 100644
> > index 00000000000..5a34395180f
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-6.C
> > @@ -0,0 +1,17 @@
> > +class bar;
> > +
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, foo *j, int k); // { dg-line decl }
> > +};
> > +
> > +// Missing '*' on a param (param 2).
> > +void foo::test (int i, foo j, int k) // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > +// { dg-message "26: parameter 2 of candidate has type 'foo\\*'"
> > "param of decl" { target *-*-* } decl }
> > +// { dg-message "28: \\.\\.\\.which does not match type 'foo'"
> > "param of defn" { target *-*-* } defn }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C
> > new file mode 100644
> > index 00000000000..12dee583aa3
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7.C
> > @@ -0,0 +1,14 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, int k, ...); // { dg-line
> > decl }
> > +};
> > +
> > +// Variadic vs non-variadic
> > +
> > +void foo::test (int i, int j, int k) // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C
> > new file mode 100644
> > index 00000000000..a484864dbac
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C
> > @@ -0,0 +1,14 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, int k); // { dg-line decl }
> > +};
> > +
> > +// Variadic vs non-variadic
> > +
> > +void foo::test (int i, int j, int k, ...) // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
> > diff --git a/gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C
> > b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C
> > new file mode 100644
> > index 00000000000..cdd691c49c0
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C
> > @@ -0,0 +1,14 @@
> > +class foo // { dg-message "'class foo' defined here" }
> > +{
> > +public:
> > + void test (int i, int j, int k); // { dg-line decl }
> > +};
> > +
> > +// Variadic vs non-variadic
> > +
> > +void foo::test (int i, int j, ...) // { dg-line defn }
> > +{
> > +}
> > +
> > +// { dg-error "6: no declaration matches" "error" { target *-*-* }
> > defn }
> > +// { dg-message "8: candidate is: " "candidate is" { target *-*-*
> > } decl }
>