https://gcc.gnu.org/g:0f1d55a75b09c13e3db09654c2b5aaad5741262f

commit r16-383-g0f1d55a75b09c13e3db09654c2b5aaad5741262f
Author: Simon Martin <si...@nasilyan.com>
Date:   Mon May 5 10:12:08 2025 +0200

    c++: Inhibit subsequent warnings/notes in diagnostic_groups with an 
inhibited warning [PR118163,PR118392]
    
    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.
    
            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.

Diff:
---
 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 +++++
 gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C   | 15 +++++
 6 files changed, 110 insertions(+), 3 deletions(-)

diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 429c4b1b116a..c3ea1b34d2f2 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -282,6 +282,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;
@@ -917,6 +918,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)
 {
@@ -991,6 +993,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.  */
@@ -1381,7 +1427,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);
@@ -1411,7 +1460,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
@@ -1421,6 +1473,10 @@ 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);
 
@@ -1436,6 +1492,9 @@ diagnostic_context::report_diagnostic (diagnostic_info 
*diagnostic)
        error_recursion ();
     }
 
+  /* 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)
@@ -1769,6 +1828,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
@@ -1781,6 +1842,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 5cde881c0743..88288d0df262 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -966,8 +966,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 2428d1a68f9d..243b3e08b112 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 000000000000..e2fb20a4ae87
--- /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 000000000000..d13021d0b681
--- /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 000000000000..bd021df5da40
--- /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 () {}

Reply via email to