From d6c90fb9f1a66409d9e483278c168bf3ca64ecc0 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 17 Jul 2025 12:34:31 -0400
Subject: [PATCH 30/41] {needs ChangeLog} move buffering code to its own .cc
 file

---
 gcc/Makefile.in              |   1 +
 gcc/diagnostic.cc            | 167 -----------------------------
 gcc/diagnostics/buffering.cc | 199 +++++++++++++++++++++++++++++++++++
 gcc/diagnostics/buffering.h  |   2 +-
 4 files changed, 201 insertions(+), 168 deletions(-)
 create mode 100644 gcc/diagnostics/buffering.cc

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index cef28379ed95..e743c94c97c4 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1853,6 +1853,7 @@ OBJS = \
 # no target dependencies.
 OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
 	diagnostic-global-context.o \
+	diagnostics/buffering.o \
 	diagnostics/digraphs.o \
 	diagnostics/edit-context.o \
 	diagnostics/output-spec.o \
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 9aecd0653cc9..ea606e34d2da 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -1742,77 +1742,6 @@ set_text_art_charset (enum diagnostic_text_art_charset charset)
     }
 }
 
-/* If BUFFER is non-null, use BUFFER as the active diagnostic_buffer on
-   this context.  BUFFER is borrowed.
-
-   If BUFFER is null, stop any buffering on this context until the next call
-   to this function.  */
-
-void
-diagnostics::context::set_diagnostic_buffer (diagnostics::buffer *buffer_)
-{
-  /* We don't allow changing buffering within a diagnostic group
-     (to simplify handling of buffered diagnostics within the
-     diagnostic_format implementations).  */
-  gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
-
-  /* Likewise, for simplicity, we only allow changing buffers
-     at nesting level 0.  */
-  gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
-
-  m_diagnostic_buffer = buffer_;
-
-  if (buffer_)
-    {
-      buffer_->ensure_per_sink_buffers ();
-      gcc_assert (buffer_->m_per_sink_buffers);
-      gcc_assert (buffer_->m_per_sink_buffers->length ()
-		  == m_sinks.length ());
-      for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
-	{
-	  auto sink_ = m_sinks[idx];
-	  auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
-	  sink_->set_buffer (per_sink_buffer);
-	}
-    }
-  else
-    for (auto sink_ : m_sinks)
-      sink_->set_buffer (nullptr);
-}
-
-/* Clear BUFFER without flushing it.  */
-
-void
-diagnostics::context::clear_diagnostic_buffer (diagnostics::buffer &buffer_)
-{
-  if (buffer_.m_per_sink_buffers)
-    for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
-      per_sink_buffer_->clear ();
-
-  buffer_.m_diagnostic_counters.clear ();
-
-  /* We need to reset last_location, otherwise we may skip caret lines
-     when we actually give a diagnostic.  */
-  m_last_location = UNKNOWN_LOCATION;
-}
-
-/* Flush the diagnostics in BUFFER to this context, clearing BUFFER.  */
-
-void
-diagnostics::context::flush_diagnostic_buffer (diagnostics::buffer &buffer_)
-{
-  bool had_errors
-    = (buffer_.m_diagnostic_counters.m_count_for_kind[DK_ERROR] > 0
-       || buffer_.m_diagnostic_counters.m_count_for_kind[DK_WERROR] > 0);
-  if (buffer_.m_per_sink_buffers)
-    for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
-      per_sink_buffer_->flush ();
-  buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
-
-  action_after_output (had_errors ? DK_ERROR : DK_WARNING);
-  check_max_errors (true);
-}
-
 /* struct diagnostics::counters.  */
 
 diagnostics::counters::counters ()
@@ -1852,102 +1781,6 @@ diagnostics::counters::clear ()
   memset (&m_count_for_kind, 0, sizeof m_count_for_kind);
 }
 
-/* class diagnostics::buffer.  */
-
-diagnostics::buffer::buffer (diagnostics::context &ctxt)
-: m_ctxt (ctxt),
-  m_per_sink_buffers (nullptr)
-{
-}
-
-diagnostics::buffer::~buffer ()
-{
-  if (m_per_sink_buffers)
-    {
-      for (auto iter : *m_per_sink_buffers)
-	delete iter;
-      delete m_per_sink_buffers;
-    }
-}
-
-void
-diagnostics::buffer::dump (FILE *out, int indent) const
-{
-  m_diagnostic_counters.dump (out, indent + 2);
-  fprintf (out, "%*sm_per_sink_buffers:\n", indent, "");
-  if (m_per_sink_buffers)
-    for (auto per_sink_buffer_ : *m_per_sink_buffers)
-      per_sink_buffer_->dump (out, indent + 2);
-  else
-    fprintf (out, "%*s(none)\n", indent + 2, "");
-}
-
-bool
-diagnostics::buffer::empty_p () const
-{
-  if (m_per_sink_buffers)
-    for (auto per_sink_buffer_ : *m_per_sink_buffers)
-      /* Query initial buffer.  */
-      return per_sink_buffer_->empty_p ();
-  return true;
-}
-
-void
-diagnostics::buffer::move_to (diagnostics::buffer &dest)
-{
-  /* Bail if there's nothing to move.  */
-  if (!m_per_sink_buffers)
-    return;
-
-  m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
-
-  if (!dest.m_per_sink_buffers)
-    {
-      /* Optimization for the "move to empty" case:
-	 simply move the vec to the dest.  */
-      dest.m_per_sink_buffers = m_per_sink_buffers;
-      m_per_sink_buffers = nullptr;
-      return;
-    }
-
-  dest.ensure_per_sink_buffers ();
-  gcc_assert (m_per_sink_buffers);
-  gcc_assert (m_per_sink_buffers->length ()
-	      == m_ctxt.m_sinks.length ());
-  gcc_assert (dest.m_per_sink_buffers);
-  gcc_assert (dest.m_per_sink_buffers->length ()
-	      == m_ctxt.m_sinks.length ());
-  for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
-    {
-      auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
-      auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
-      per_sink_buffer_src->move_to (*per_sink_buffer_dest);
-    }
-}
-
-/* Lazily get the output formats to create their own kind of buffers.
-   We can't change the output sinks on a context once this has been called
-   on any diagnostic_buffer instances for that context, since there's no
-   way to update all diagnostic_buffer instances for that context.  */
-
-void
-diagnostics::buffer::ensure_per_sink_buffers ()
-{
-  if (!m_per_sink_buffers)
-    {
-      m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
-      for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
-	{
-	  auto sink_ = m_ctxt.m_sinks[idx];
-	  auto per_sink_buffer = sink_->make_per_sink_buffer ();
-	  m_per_sink_buffers->safe_push (per_sink_buffer.release ());
-	}
-    }
-  gcc_assert (m_per_sink_buffers);
-  gcc_assert (m_per_sink_buffers->length ()
-	      == m_ctxt.m_sinks.length ());
-}
-
 /* Really call the system 'abort'.  This has to go right at the end of
    this file, so that there are no functions after it that call abort
    and get the system abort instead of our macro.  */
diff --git a/gcc/diagnostics/buffering.cc b/gcc/diagnostics/buffering.cc
new file mode 100644
index 000000000000..53afc90f02b7
--- /dev/null
+++ b/gcc/diagnostics/buffering.cc
@@ -0,0 +1,199 @@
+/* Support for buffering diagnostics before flushing them to output sinks.
+   Copyright (C) 2024-2025 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "diagnostics/buffering.h"
+#include "diagnostics/sink.h"
+
+namespace diagnostics {
+
+/* Methods fns of diagnostics::context relating to buffering.  */
+
+/* If BUFFER_ is non-null, use BUFFER as the active diagnostics::buffer on
+   this context.  BUFFER is borrowed.
+
+   If BUFFER_ is null, stop any buffering on this context until the next call
+   to this function.  */
+
+void
+context::set_diagnostic_buffer (buffer *buffer_)
+{
+  /* We don't allow changing buffering within a diagnostic group
+     (to simplify handling of buffered diagnostics within the
+     diagnostic_format implementations).  */
+  gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
+
+  /* Likewise, for simplicity, we only allow changing buffers
+     at nesting level 0.  */
+  gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
+
+  m_diagnostic_buffer = buffer_;
+
+  if (buffer_)
+    {
+      buffer_->ensure_per_sink_buffers ();
+      gcc_assert (buffer_->m_per_sink_buffers);
+      gcc_assert (buffer_->m_per_sink_buffers->length ()
+		  == m_sinks.length ());
+      for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
+	{
+	  auto sink_ = m_sinks[idx];
+	  auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
+	  sink_->set_buffer (per_sink_buffer);
+	}
+    }
+  else
+    for (auto sink_ : m_sinks)
+      sink_->set_buffer (nullptr);
+}
+
+/* Clear BUFFER_ without flushing it.  */
+
+void
+context::clear_diagnostic_buffer (buffer &buffer_)
+{
+  if (buffer_.m_per_sink_buffers)
+    for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
+      per_sink_buffer_->clear ();
+
+  buffer_.m_diagnostic_counters.clear ();
+
+  /* We need to reset last_location, otherwise we may skip caret lines
+     when we actually give a diagnostic.  */
+  m_last_location = UNKNOWN_LOCATION;
+}
+
+/* Flush the diagnostics in BUFFER_ to this context, clearing BUFFER_.  */
+
+void
+context::flush_diagnostic_buffer (buffer &buffer_)
+{
+  bool had_errors
+    = (buffer_.m_diagnostic_counters.m_count_for_kind[DK_ERROR] > 0
+       || buffer_.m_diagnostic_counters.m_count_for_kind[DK_WERROR] > 0);
+  if (buffer_.m_per_sink_buffers)
+    for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
+      per_sink_buffer_->flush ();
+  buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
+
+  action_after_output (had_errors ? DK_ERROR : DK_WARNING);
+  check_max_errors (true);
+}
+
+/* class diagnostics::buffer.  */
+
+buffer::buffer (context &ctxt)
+: m_ctxt (ctxt),
+  m_per_sink_buffers (nullptr)
+{
+}
+
+buffer::~buffer ()
+{
+  if (m_per_sink_buffers)
+    {
+      for (auto iter : *m_per_sink_buffers)
+	delete iter;
+      delete m_per_sink_buffers;
+    }
+}
+
+void
+buffer::dump (FILE *out, int indent) const
+{
+  m_diagnostic_counters.dump (out, indent + 2);
+  fprintf (out, "%*sm_per_sink_buffers:\n", indent, "");
+  if (m_per_sink_buffers)
+    for (auto per_sink_buffer_ : *m_per_sink_buffers)
+      per_sink_buffer_->dump (out, indent + 2);
+  else
+    fprintf (out, "%*s(none)\n", indent + 2, "");
+}
+
+bool
+buffer::empty_p () const
+{
+  if (m_per_sink_buffers)
+    for (auto per_sink_buffer_ : *m_per_sink_buffers)
+      /* Query initial buffer.  */
+      return per_sink_buffer_->empty_p ();
+  return true;
+}
+
+void
+buffer::move_to (buffer &dest)
+{
+  /* Bail if there's nothing to move.  */
+  if (!m_per_sink_buffers)
+    return;
+
+  m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
+
+  if (!dest.m_per_sink_buffers)
+    {
+      /* Optimization for the "move to empty" case:
+	 simply move the vec to the dest.  */
+      dest.m_per_sink_buffers = m_per_sink_buffers;
+      m_per_sink_buffers = nullptr;
+      return;
+    }
+
+  dest.ensure_per_sink_buffers ();
+  gcc_assert (m_per_sink_buffers);
+  gcc_assert (m_per_sink_buffers->length ()
+	      == m_ctxt.m_sinks.length ());
+  gcc_assert (dest.m_per_sink_buffers);
+  gcc_assert (dest.m_per_sink_buffers->length ()
+	      == m_ctxt.m_sinks.length ());
+  for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
+    {
+      auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
+      auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
+      per_sink_buffer_src->move_to (*per_sink_buffer_dest);
+    }
+}
+
+/* Lazily get the output formats to create their own kind of buffers.
+   We can't change the output sinks on a context once this has been called
+   on any diagnostics::buffer instances for that context, since there's no
+   way to update all diagnostics::buffer instances for that context.  */
+
+void
+buffer::ensure_per_sink_buffers ()
+{
+  if (!m_per_sink_buffers)
+    {
+      m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
+      for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
+	{
+	  auto sink_ = m_ctxt.m_sinks[idx];
+	  auto per_sink_buffer = sink_->make_per_sink_buffer ();
+	  m_per_sink_buffers->safe_push (per_sink_buffer.release ());
+	}
+    }
+  gcc_assert (m_per_sink_buffers);
+  gcc_assert (m_per_sink_buffers->length ()
+	      == m_ctxt.m_sinks.length ());
+}
+
+} // namespace diagnostics
diff --git a/gcc/diagnostics/buffering.h b/gcc/diagnostics/buffering.h
index 3ab0fce54da5..1e65b4e37b6f 100644
--- a/gcc/diagnostics/buffering.h
+++ b/gcc/diagnostics/buffering.h
@@ -1,4 +1,4 @@
-/* Support for buffering diagnostics before flushing them to output format.
+/* Support for buffering diagnostics before flushing them to output sinks.
    Copyright (C) 2024-2025 Free Software Foundation, Inc.
    Contributed by David Malcolm <dmalcolm@redhat.com>.
 
-- 
2.26.3

