diff options
Diffstat (limited to 'gcc/diagnostics/buffering.cc')
-rw-r--r-- | gcc/diagnostics/buffering.cc | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/gcc/diagnostics/buffering.cc b/gcc/diagnostics/buffering.cc new file mode 100644 index 0000000..29f039f --- /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_.diagnostic_count (kind::error) > 0 + || buffer_.diagnostic_count (kind::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 ? kind::error : kind::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 |