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 2742906 : 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 2742906 : 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 2435670 : 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 2435670 : gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
55 :
56 2435670 : m_diagnostic_buffer = buffer_;
57 :
58 2435670 : if (buffer_)
59 : {
60 1217839 : buffer_->ensure_per_sink_buffers ();
61 1217839 : gcc_assert (buffer_->m_per_sink_buffers);
62 3653517 : gcc_assert (buffer_->m_per_sink_buffers->length ()
63 : == m_sinks.length ());
64 2435678 : for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
65 : {
66 1217839 : auto sink_ = m_sinks[idx];
67 1217839 : auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
68 1217839 : sink_->set_buffer (per_sink_buffer);
69 : }
70 : }
71 : else
72 4871324 : for (auto sink_ : m_sinks)
73 1217831 : sink_->set_buffer (nullptr);
74 : }
75 :
76 : /* Clear BUFFER_ without flushing it. */
77 :
78 : void
79 23302109 : context::clear_diagnostic_buffer (buffer &buffer_)
80 : {
81 23302109 : if (buffer_.m_per_sink_buffers)
82 20590116 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
83 5147529 : per_sink_buffer_->clear ();
84 :
85 23302109 : 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 23302109 : m_last_location = UNKNOWN_LOCATION;
90 23302109 : }
91 :
92 : /* Flush the diagnostics in BUFFER_ to this context, clearing BUFFER_. */
93 :
94 : void
95 7317 : context::flush_diagnostic_buffer (buffer &buffer_)
96 : {
97 7317 : bool had_errors
98 7317 : = (buffer_.diagnostic_count (kind::error) > 0
99 7317 : || buffer_.diagnostic_count (kind::werror) > 0);
100 7317 : if (buffer_.m_per_sink_buffers)
101 29268 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
102 7317 : per_sink_buffer_->flush ();
103 7317 : buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
104 :
105 11393 : action_after_output (had_errors ? kind::error : kind::warning);
106 7317 : check_max_errors (true);
107 7314 : }
108 :
109 : /* class diagnostics::buffer. */
110 :
111 8546018 : buffer::buffer (context &ctxt)
112 8546018 : : m_ctxt (ctxt),
113 8546018 : m_per_sink_buffers (nullptr)
114 : {
115 8546018 : }
116 :
117 8514606 : buffer::~buffer ()
118 : {
119 8514606 : if (m_per_sink_buffers)
120 : {
121 384056 : for (auto iter : *m_per_sink_buffers)
122 96014 : delete iter;
123 192028 : delete m_per_sink_buffers;
124 : }
125 8514606 : }
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 6112743 : buffer::empty_p () const
141 : {
142 6112743 : if (m_per_sink_buffers)
143 9782352 : for (auto per_sink_buffer_ : *m_per_sink_buffers)
144 : /* Query initial buffer. */
145 3260784 : return per_sink_buffer_->empty_p ();
146 : return true;
147 : }
148 :
149 : void
150 106258 : buffer::move_to (buffer &dest)
151 : {
152 : /* Bail if there's nothing to move. */
153 106258 : if (!m_per_sink_buffers)
154 : return;
155 :
156 106258 : m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
157 :
158 106258 : if (!dest.m_per_sink_buffers)
159 : {
160 : /* Optimization for the "move to empty" case:
161 : simply move the vec to the dest. */
162 82885 : dest.m_per_sink_buffers = m_per_sink_buffers;
163 82885 : m_per_sink_buffers = nullptr;
164 82885 : return;
165 : }
166 :
167 23373 : dest.ensure_per_sink_buffers ();
168 23373 : gcc_assert (m_per_sink_buffers);
169 70119 : gcc_assert (m_per_sink_buffers->length ()
170 : == m_ctxt.m_sinks.length ());
171 23373 : gcc_assert (dest.m_per_sink_buffers);
172 46746 : gcc_assert (dest.m_per_sink_buffers->length ()
173 : == m_ctxt.m_sinks.length ());
174 46746 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
175 : {
176 23373 : auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
177 23373 : auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
178 23373 : 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 1241212 : buffer::ensure_per_sink_buffers ()
189 : {
190 1241212 : if (!m_per_sink_buffers)
191 : {
192 98241 : m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
193 196482 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
194 : {
195 98241 : auto sink_ = m_ctxt.m_sinks[idx];
196 98241 : auto per_sink_buffer = sink_->make_per_sink_buffer ();
197 98241 : m_per_sink_buffers->safe_push (per_sink_buffer.release ());
198 98241 : }
199 : }
200 1241212 : gcc_assert (m_per_sink_buffers);
201 3723636 : gcc_assert (m_per_sink_buffers->length ()
202 : == m_ctxt.m_sinks.length ());
203 1241212 : }
204 :
205 : } // namespace diagnostics
|