]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Inhibit subsequent warnings/notes in diagnostic_groups with an inhibited warning...
authorSimon Martin <simon@nasilyan.com>
Mon, 5 May 2025 08:12:08 +0000 (10:12 +0200)
committerSimon Martin <simon@nasilyan.com>
Mon, 5 May 2025 08:26:13 +0000 (10:26 +0200)
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.

gcc/diagnostic.cc
gcc/diagnostic.h
gcc/doc/ux.texi
gcc/testsuite/g++.dg/diagnostic/incomplete-type-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/incomplete-type-2a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/inhibit-warn-3.C [new file with mode: 0644]

index 429c4b1b116a5df83e185070b967d9a23304363a..c3ea1b34d2f2544f0901f1d16aa53bf61992f171 100644 (file)
@@ -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
index 5cde881c0743f6e0ce16ccc1bafbb67432542e8a..88288d0df2626c642f9f34a3573452ea1b9d814e 100644 (file)
@@ -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
index 2428d1a68f9ded4ec180094f28c8759c04366959..243b3e08b1121bac473b054d8f7d3da4a55d17b9 100644 (file)
@@ -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 (file)
index 0000000..e2fb20a
--- /dev/null
@@ -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 (file)
index 0000000..d13021d
--- /dev/null
@@ -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 (file)
index 0000000..bd021df
--- /dev/null
@@ -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 () {}