LCOV - code coverage report
Current view: top level - gcc/diagnostics - buffering.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 90.2 % 82 74
Test Date: 2026-02-28 14:20:25 Functions: 88.9 % 9 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Support for buffering diagnostics before flushing them to output sinks.
       2              :    Copyright (C) 2024-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it under
       8              : the terms of the GNU General Public License as published by the Free
       9              : Software Foundation; either version 3, or (at your option) any later
      10              : version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15              : for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : #define INCLUDE_VECTOR
      22              : #include "config.h"
      23              : #include "system.h"
      24              : #include "coretypes.h"
      25              : #include "diagnostics/buffering.h"
      26              : #include "diagnostics/sink.h"
      27              : #include "diagnostics/dumping.h"
      28              : 
      29              : namespace diagnostics {
      30              : 
      31              : /* Methods fns of diagnostics::context relating to buffering.  */
      32              : 
      33              : /* If BUFFER_ is non-null, use BUFFER as the active diagnostics::buffer on
      34              :    this context.  BUFFER is borrowed.
      35              : 
      36              :    If BUFFER_ is null, stop any buffering on this context until the next call
      37              :    to this function.  */
      38              : 
      39              : void
      40      2737515 : context::set_diagnostic_buffer (buffer *buffer_)
      41              : {
      42              :   /* Early reject of no-op (to avoid recursively crashing when handling an
      43              :      ICE when inside a nested diagnostics; see PR diagnostics/121876).  */
      44      2737515 :   if (buffer_ == m_diagnostic_buffer)
      45              :     return;
      46              : 
      47              :   /* We don't allow changing buffering within a diagnostic group
      48              :      (to simplify handling of buffered diagnostics within the
      49              :      diagnostic_format implementations).  */
      50      2432422 :   gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
      51              : 
      52              :   /* Likewise, for simplicity, we only allow changing buffers
      53              :      at nesting level 0.  */
      54      2432422 :   gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
      55              : 
      56      2432422 :   m_diagnostic_buffer = buffer_;
      57              : 
      58      2432422 :   if (buffer_)
      59              :     {
      60      1216215 :       buffer_->ensure_per_sink_buffers ();
      61      1216215 :       gcc_assert (buffer_->m_per_sink_buffers);
      62      3648645 :       gcc_assert (buffer_->m_per_sink_buffers->length ()
      63              :                   == m_sinks.length ());
      64      2432430 :       for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
      65              :         {
      66      1216215 :           auto sink_ = m_sinks[idx];
      67      1216215 :           auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
      68      1216215 :           sink_->set_buffer (per_sink_buffer);
      69              :         }
      70              :     }
      71              :   else
      72      4864828 :     for (auto sink_ : m_sinks)
      73      1216207 :       sink_->set_buffer (nullptr);
      74              : }
      75              : 
      76              : /* Clear BUFFER_ without flushing it.  */
      77              : 
      78              : void
      79     23260750 : context::clear_diagnostic_buffer (buffer &buffer_)
      80              : {
      81     23260750 :   if (buffer_.m_per_sink_buffers)
      82     20564688 :     for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
      83      5141172 :       per_sink_buffer_->clear ();
      84              : 
      85     23260750 :   buffer_.m_diagnostic_counters.clear ();
      86              : 
      87              :   /* We need to reset last_location, otherwise we may skip caret lines
      88              :      when we actually give a diagnostic.  */
      89     23260750 :   m_last_location = UNKNOWN_LOCATION;
      90     23260750 : }
      91              : 
      92              : /* Flush the diagnostics in BUFFER_ to this context, clearing BUFFER_.  */
      93              : 
      94              : void
      95         7291 : context::flush_diagnostic_buffer (buffer &buffer_)
      96              : {
      97         7291 :   bool had_errors
      98         7291 :     = (buffer_.diagnostic_count (kind::error) > 0
      99         7291 :        || buffer_.diagnostic_count (kind::werror) > 0);
     100         7291 :   if (buffer_.m_per_sink_buffers)
     101        29164 :     for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
     102         7291 :       per_sink_buffer_->flush ();
     103         7291 :   buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
     104              : 
     105        11365 :   action_after_output (had_errors ? kind::error : kind::warning);
     106         7291 :   check_max_errors (true);
     107         7288 : }
     108              : 
     109              : /* class diagnostics::buffer.  */
     110              : 
     111      8537729 : buffer::buffer (context &ctxt)
     112      8537729 : : m_ctxt (ctxt),
     113      8537729 :   m_per_sink_buffers (nullptr)
     114              : {
     115      8537729 : }
     116              : 
     117      8506389 : buffer::~buffer ()
     118              : {
     119      8506389 :   if (m_per_sink_buffers)
     120              :     {
     121       383408 :       for (auto iter : *m_per_sink_buffers)
     122        95852 :         delete iter;
     123       191704 :       delete m_per_sink_buffers;
     124              :     }
     125      8506389 : }
     126              : 
     127              : void
     128            0 : buffer::dump (FILE *out, int indent) const
     129              : {
     130            0 :   m_diagnostic_counters.dump (out, indent + 2);
     131            0 :   dumping::emit_heading (out, indent, "m_per_sink_buffers");
     132            0 :   if (m_per_sink_buffers)
     133            0 :     for (auto per_sink_buffer_ : *m_per_sink_buffers)
     134            0 :       per_sink_buffer_->dump (out, indent + 2);
     135              :   else
     136            0 :     dumping::emit_none (out, indent + 2);
     137            0 : }
     138              : 
     139              : bool
     140      6105094 : buffer::empty_p () const
     141              : {
     142      6105094 :   if (m_per_sink_buffers)
     143      9772512 :     for (auto per_sink_buffer_ : *m_per_sink_buffers)
     144              :       /* Query initial buffer.  */
     145      3257504 :       return per_sink_buffer_->empty_p ();
     146              :   return true;
     147              : }
     148              : 
     149              : void
     150       106002 : buffer::move_to (buffer &dest)
     151              : {
     152              :   /* Bail if there's nothing to move.  */
     153       106002 :   if (!m_per_sink_buffers)
     154              :     return;
     155              : 
     156       106002 :   m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
     157              : 
     158       106002 :   if (!dest.m_per_sink_buffers)
     159              :     {
     160              :       /* Optimization for the "move to empty" case:
     161              :          simply move the vec to the dest.  */
     162        82705 :       dest.m_per_sink_buffers = m_per_sink_buffers;
     163        82705 :       m_per_sink_buffers = nullptr;
     164        82705 :       return;
     165              :     }
     166              : 
     167        23297 :   dest.ensure_per_sink_buffers ();
     168        23297 :   gcc_assert (m_per_sink_buffers);
     169        69891 :   gcc_assert (m_per_sink_buffers->length ()
     170              :               == m_ctxt.m_sinks.length ());
     171        23297 :   gcc_assert (dest.m_per_sink_buffers);
     172        46594 :   gcc_assert (dest.m_per_sink_buffers->length ()
     173              :               == m_ctxt.m_sinks.length ());
     174        46594 :   for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
     175              :     {
     176        23297 :       auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
     177        23297 :       auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
     178        23297 :       per_sink_buffer_src->move_to (*per_sink_buffer_dest);
     179              :     }
     180              : }
     181              : 
     182              : /* Lazily get the output formats to create their own kind of buffers.
     183              :    We can't change the output sinks on a context once this has been called
     184              :    on any diagnostics::buffer instances for that context, since there's no
     185              :    way to update all diagnostics::buffer instances for that context.  */
     186              : 
     187              : void
     188      1239512 : buffer::ensure_per_sink_buffers ()
     189              : {
     190      1239512 :   if (!m_per_sink_buffers)
     191              :     {
     192        98076 :       m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
     193       196152 :       for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
     194              :         {
     195        98076 :           auto sink_ = m_ctxt.m_sinks[idx];
     196        98076 :           auto per_sink_buffer = sink_->make_per_sink_buffer ();
     197        98076 :           m_per_sink_buffers->safe_push (per_sink_buffer.release ());
     198        98076 :         }
     199              :     }
     200      1239512 :   gcc_assert (m_per_sink_buffers);
     201      3718536 :   gcc_assert (m_per_sink_buffers->length ()
     202              :               == m_ctxt.m_sinks.length ());
     203      1239512 : }
     204              : 
     205              : } // namespace diagnostics
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.