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
|