[ Fixing David’s email address :-/ ] Hi,
On 9 Jan 2025, at 20:08, Simon Martin wrote: > On 9 Jan 2025, at 20:00, Marek Polacek wrote: > >> On Thu, Jan 09, 2025 at 12:05:43PM -0500, Patrick Palka wrote: >>> On Wed, 8 Jan 2025, Jason Merrill wrote: >>> >>>> On 12/21/24 11:35 AM, Simon Martin wrote: >>>>> When erroring out due to an incomplete type, we add a contextual >>>>> note >>>>> about the type. However, when the error is suppressed by >>>>> -Wno-template-body, the note remains, making the compiler output >>>>> quite >>>>> puzzling. >>>>> >>>>> This patch makes sure the note is suppressed if we're processing a >>>>> template declaration body with -Wno-template-body. >>>>> >>>>> Successfully tested on x86_64-pc-linux-gnu. >>>>> >>>>> PR c++/118163 >>>>> >>>>> gcc/cp/ChangeLog: >>>>> >>>>> * cp-tree.h (get_current_template): Declare. >>>>> * error.cc (get_current_template): Make non static. >>>>> * typeck2.cc (cxx_incomplete_type_inform): Suppress note when >>>>> parsing a template declaration with -Wno-template-body. >>>> >>>> I think rather than adding this sort of thing in lots of places >>>> where an error >>>> is followed by an inform, we should change error to return bool >>>> like >>>> other >>>> diagnostic functions, and check its return value before calling >>>> cxx_incomplete_type_inform or plain inform. This likely involves >>>> the same >>>> number of changes, but they should be smaller. >>>> >>>> Patrick, what do you think? >>> >>> That makes sense to me, it's consistent with the 'warning' API and >>> how >>> we handle issuing a warning followed by a note. But since the >>> -Wtemplate-body mechanism is really only useful for compiling legacy >>> code where you don't really care about any diagnostics anyway, and >>> the intended way to use it is -fpermissive / >>> -Wno-error=template-body >>> rather than -Wno-template-body, I'd prefer a less invasive solution >>> that >>> doesn't change the API of 'error' if possible. >>> >>> I wonder if we can work around this by taking advantage of the fact >>> that >>> notes that follow an error are expected to be linked via an active >>> auto_diagnostic_group? Roughly, if we issued a -Wtemplate-body >>> diagnostic from an active auto_diagnostic_group then all other >>> diagnostics from that auto_diagnostic_group should also be >>> associated >>> with -Wtemplate-body, including notes. That way -Wno-template-body >>> will >>> effectively suppress subsequent notes followed by an eligible error, >>> and >>> no 'error' callers need to be changed (unless to use >>> auto_diagnostic_group). >> >> FWIW, I love this auto_diagnostic_group idea. > Thanks folks, I’ll explore the auto_diagnostic_group idea (and maybe > *also* the error returning bool one because I am not a fan of > functions > that “lie” to their callers :-)) > > I’ll send a follow-up patch in the coming days. Please find attached an updated version of the patch, that implements Patrick’s idea and fixes both PR118163 and PR118392. It tracks the depth at which a warning is inhibited, and suppresses all the notes from that depth on until an error/warning is emitted or that depth is left. Successfully tested on x86_64-pc-linux-gnu. OK for GCC 16? Thanks, Simon
From 865472e427aba34ba07a71e85c8a52619fe381ce Mon Sep 17 00:00:00 2001 From: Simon Martin <si...@nasilyan.com> Date: Sun, 12 Jan 2025 10:53:00 +0100 Subject: [PATCH] c++: Inhibit subsequent warnings/notes in diagnostic_groups with an inhibited warning [PR118163,PR118392] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those 2 PRs show that even when using a *single* diagnostic_group, it's possible to end up with a warning being inhibited and its associated note still emitted, which leads to puzzling user experience. Example from PR118392: === $ gcc/cc1plus inhibit-warn-3.C -w inhibit-warn-3.C:10:17: note: only here as a âfriendâ 10 | friend void bar(); | ^~~ === Following a suggestion from ppalka@, this patch keeps track of the "diagnostic depth" at which a warning in inhibited, and makes sure that all subsequent notes at that depth or deeper are inhibited as well, until a subsequent warning or error is accepted at that depth, or the diagnostic_group or nesting level is popped. I briefly checked the selftests mechanism in diagnostic.cc and I don't think this is something that we can add a test for there. Let me know if I'm wrong. Successfully tested on x86_64-pc-linux-gnu. OK for GCC 16? PR c++/118163 PR c++/118392 gcc/ChangeLog: * diagnostic.cc (diagnostic_context::initialize): Initialize m_diagnostic_groups.m_inhibiting_notes_from. (diagnostic_context::inhibit_notes_in_group): New. (diagnostic_context::notes_inhibited_in_group): New (diagnostic_context::report_diagnostic): Call inhibit_notes_in_group and notes_inhibited_in_group. (diagnostic_context::end_group): Call inhibit_notes_in_group. (diagnostic_context::pop_nesting_level): Ditto. * diagnostic.h (diagnostic_context::m_diagnostic_groups): Add member to track the depth at which a warning has been inhibited. (diagnostic_context::notes_inhibited_in_group): Declare. (diagnostic_context::inhibit_notes_in_group): Declare. * doc/ux.texi: Document diagnostic_group behavior with regards to inhibited warnings. gcc/testsuite/ChangeLog: * g++.dg/diagnostic/incomplete-type-2.C: New test. * g++.dg/diagnostic/incomplete-type-2a.C: New test. * g++.dg/diagnostic/inhibit-warn-3.C: New test. --- gcc/diagnostic.cc | 67 ++++++++++++++++++- gcc/diagnostic.h | 6 ++ gcc/doc/ux.texi | 5 +- .../g++.dg/diagnostic/incomplete-type-2.C | 7 ++ .../g++.dg/diagnostic/incomplete-type-2a.C | 13 ++++ .../g++.dg/diagnostic/inhibit-warn-3.C | 15 +++++ 6 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/diagnostic/incomplete-type-2.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/incomplete-type-2a.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index c2f6714c24a..310497f4309 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -283,6 +283,7 @@ diagnostic_context::initialize (int n_opts) m_diagnostic_groups.m_group_nesting_depth = 0; m_diagnostic_groups.m_diagnostic_nesting_level = 0; m_diagnostic_groups.m_emission_count = 0; + m_diagnostic_groups.m_inhibiting_notes_from = 0; m_output_sinks.safe_push (new diagnostic_text_output_format (*this, nullptr, true)); m_set_locations_cb = nullptr; @@ -891,6 +892,7 @@ diagnostic_context::check_max_errors (bool flush) /* Take any action which is expected to happen after the diagnostic is written out. This function does not always return. */ + void diagnostic_context::action_after_output (diagnostic_t diag_kind) { @@ -965,6 +967,50 @@ diagnostic_context::action_after_output (diagnostic_t diag_kind) } } +/* State whether we should inhibit notes in the current diagnostic_group and + its future children if any. */ + +void +diagnostic_context::inhibit_notes_in_group (bool inhibit) +{ + int curr_depth = (m_diagnostic_groups.m_group_nesting_depth + + m_diagnostic_groups.m_diagnostic_nesting_level); + + if (inhibit) + { + /* If we're already inhibiting, there's nothing to do. */ + if (m_diagnostic_groups.m_inhibiting_notes_from) + return; + + /* Since we're called via warning/error/... that all have their own + diagnostic_group, we must consider that we started inhibiting in their + parent. */ + gcc_assert (m_diagnostic_groups.m_group_nesting_depth > 0); + m_diagnostic_groups.m_inhibiting_notes_from = curr_depth - 1; + } + else if (m_diagnostic_groups.m_inhibiting_notes_from) + { + /* Only cancel inhibition at the depth that set it up. */ + if (curr_depth >= m_diagnostic_groups.m_inhibiting_notes_from) + return; + + m_diagnostic_groups.m_inhibiting_notes_from = 0; + } +} + +/* Return whether notes must be inhibited in the current diagnostic_group. */ + +bool +diagnostic_context::notes_inhibited_in_group () const +{ + if (m_diagnostic_groups.m_inhibiting_notes_from + && (m_diagnostic_groups.m_group_nesting_depth + + m_diagnostic_groups.m_diagnostic_nesting_level + >= m_diagnostic_groups.m_inhibiting_notes_from)) + return true; + return false; +} + /* class logical_location. */ /* Return true iff this is a function or method. */ @@ -1355,7 +1401,10 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic) bool was_warning = (diagnostic->kind == DK_WARNING || diagnostic->kind == DK_PEDWARN); if (was_warning && m_inhibit_warnings) - return false; + { + inhibit_notes_in_group (); + return false; + } if (m_adjust_diagnostic_info) m_adjust_diagnostic_info (this, diagnostic); @@ -1397,7 +1446,10 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic) not disabled by #pragma GCC diagnostic anywhere along the inlining stack. . */ if (!diagnostic_enabled (diagnostic)) - return false; + { + inhibit_notes_in_group (); + return false; + } if ((was_warning || diagnostic->kind == DK_WARNING) && ((!m_warn_system_headers @@ -1407,9 +1459,16 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic) inlining stack (if there is one) are in system headers. */ return false; + if (diagnostic->kind == DK_NOTE && notes_inhibited_in_group ()) + /* Bail for all the notes in the diagnostic_group that started to inhibit notes. */ + return false; + if (diagnostic->kind != DK_NOTE && diagnostic->kind != DK_ICE) check_max_errors (false); + /* We are accepting the diagnostic, so should stop inhibiting notes. */ + inhibit_notes_in_group (/*inhibit=*/false); + m_lock++; if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT) @@ -1743,6 +1802,8 @@ diagnostic_context::end_group () sink->on_end_group (); m_diagnostic_groups.m_emission_count = 0; } + /* We're popping one level, so might need to stop inhibiting notes. */ + inhibit_notes_in_group (/*inhibit=*/false); } void @@ -1755,6 +1816,8 @@ void diagnostic_context::pop_nesting_level () { --m_diagnostic_groups.m_diagnostic_nesting_level; + /* We're popping one level, so might need to stop inhibiting notes. */ + inhibit_notes_in_group (/*inhibit=*/false); } void diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 202760b2f85..74ec61fe5ec 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -950,8 +950,14 @@ private: /* How many diagnostics have been emitted since the bottommost diagnostic_group was pushed. */ int m_emission_count; + + /* The "group+diagnostic" nesting depth from which to inhibit notes. */ + int m_inhibiting_notes_from; } m_diagnostic_groups; + void inhibit_notes_in_group (bool inhibit = true); + bool notes_inhibited_in_group () const; + /* The various sinks to which diagnostics are to be outputted (text vs structured formats such as SARIF). The sinks are owned by the context; this would be a diff --git a/gcc/doc/ux.texi b/gcc/doc/ux.texi index 2428d1a68f9..243b3e08b11 100644 --- a/gcc/doc/ux.texi +++ b/gcc/doc/ux.texi @@ -409,7 +409,10 @@ diagnostic subsystem that all diagnostics issued within the lifetime of the @code{auto_diagnostic_group} are related. For example, @option{-fdiagnostics-format=json} will treat the first diagnostic emitted within the group as a top-level diagnostic, and all subsequent -diagnostics within the group as its children. +diagnostics within the group as its children. Also, if a warning in the +group is inhibited at nesting depth D, all subsequent notes at that depth +or deeper will be inhibited as well, until an error or another warning +is emitted, the depth decreases below D, or the group is popped. @subsection Quoting Text should be quoted by either using the @samp{q} modifier in a directive diff --git a/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2.C b/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2.C new file mode 100644 index 00000000000..e2fb20a4ae8 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2.C @@ -0,0 +1,7 @@ +// PR c++/118163 +// { dg-do "compile" } + +template<class T> +struct S { // { dg-note "until the closing brace" } + S s; // { dg-error "has incomplete type" } +}; diff --git a/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2a.C b/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2a.C new file mode 100644 index 00000000000..d13021d0b68 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/incomplete-type-2a.C @@ -0,0 +1,13 @@ +// PR c++/118163 +// { dg-do "compile" } +// { dg-additional-options "-Wno-template-body" } + +template<class T> +struct S { // { dg-bogus "until the closing brace" } + S s; // { dg-bogus "has incomplete type" } +}; + +// Check that we don't suppress errors outside of the body. +struct forward_decl; // { dg-note "forward declaration" } +template<class T> +void foo (forward_decl) {} // { dg-error "has incomplete type" } diff --git a/gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C b/gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C new file mode 100644 index 00000000000..bd021df5da4 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C @@ -0,0 +1,15 @@ +// PR c++/118392 +// { dg-do "compile" } +// { dg-additional-options "-w" } + +// Fake dg-note to make sure that notes are not pruned and we can use dg-bogus. +// { dg-note "fake" "" { xfail *-*-* } } + +namespace xxx { + struct foo { + friend void bar(); // { dg-bogus "only here as a friend" } + }; +} +void xxx::bar () {} + +void foo () {} -- 2.44.0