Thanks
Dave
Changed in v4:
* reimplement fndecl_signature::m_variadic; don't emit "close match"
notes if variadicness differs; add bad-fndef-7b.C
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-7b.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]>
---
gcc/cp/call.cc | 3 +
gcc/cp/cp-tree.h | 15 +-
gcc/cp/decl2.cc | 208 +++++++++++++++++-
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 ++
.../g++.dg/diagnostic/bad-fndef-7b.C | 17 ++
gcc/testsuite/g++.dg/diagnostic/bad-fndef-8.C | 14 ++
gcc/testsuite/g++.dg/diagnostic/bad-fndef-9.C | 14 ++
14 files changed, 401 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-7b.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..da029839d8d 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,207 @@ 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)),
+ m_variadic (true)
+ {
+ function_args_iterator iter;
+ tree argtype;
+ FOREACH_FUNCTION_ARGS (TREE_TYPE (fndecl), argtype, iter)
+ {
+ /* A trailing "void" type means that FNDECL is non-variadic. */
+ if (VOID_TYPE_P (argtype))
+ {
+ gcc_assert (!TREE_CHAIN (iter.next));
+ m_variadic = false;
+ 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;
+ bool m_variadic;
+};
+
+/* 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
+ {
+ /* If there's a difference in "variadicness", don't attempt to emit
+ any notes. */
+ if (m_decl_a.m_variadic != m_decl_b.m_variadic)
+ return;
+
+ 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 +1120,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-7b.C
b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C
new file mode 100644
index 00000000000..34a899f05a4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/bad-fndef-7b.C
@@ -0,0 +1,17 @@
+class foo // { dg-message "'class foo' defined here" }
+{
+public:
+ void test (int i, int j, const char *k, ...); // { dg-line decl }
+};
+
+// Variadic vs non-variadic *and* a mismatching param
+
+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 }
+
+// The candidate is too different from the decl for a "close match" hint:
+// { dg-bogus "parameter 3 of candidate has type" "param mismatch" { 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 }