Branch data Line data Source code
1 : : /* Support for buffering diagnostics before flushing them to output sinks.
2 : : Copyright (C) 2024-2025 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 : : #include "config.h"
22 : : #include "system.h"
23 : : #include "coretypes.h"
24 : : #include "diagnostics/buffering.h"
25 : : #include "diagnostics/sink.h"
26 : : #include "diagnostics/dumping.h"
27 : :
28 : : namespace diagnostics {
29 : :
30 : : /* Methods fns of diagnostics::context relating to buffering. */
31 : :
32 : : /* If BUFFER_ is non-null, use BUFFER as the active diagnostics::buffer on
33 : : this context. BUFFER is borrowed.
34 : :
35 : : If BUFFER_ is null, stop any buffering on this context until the next call
36 : : to this function. */
37 : :
38 : : void
39 : 2686841 : context::set_diagnostic_buffer (buffer *buffer_)
40 : : {
41 : : /* Early reject of no-op (to avoid recursively crashing when handling an
42 : : ICE when inside a nested diagnostics; see PR diagnostics/121876). */
43 : 2686841 : if (buffer_ == m_diagnostic_buffer)
44 : : return;
45 : :
46 : : /* We don't allow changing buffering within a diagnostic group
47 : : (to simplify handling of buffered diagnostics within the
48 : : diagnostic_format implementations). */
49 : 2382048 : gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
50 : :
51 : : /* Likewise, for simplicity, we only allow changing buffers
52 : : at nesting level 0. */
53 : 2382048 : gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
54 : :
55 : 2382048 : m_diagnostic_buffer = buffer_;
56 : :
57 : 2382048 : if (buffer_)
58 : : {
59 : 1191028 : buffer_->ensure_per_sink_buffers ();
60 : 1191028 : gcc_assert (buffer_->m_per_sink_buffers);
61 : 3573084 : gcc_assert (buffer_->m_per_sink_buffers->length ()
62 : : == m_sinks.length ());
63 : 2382056 : for (unsigned idx = 0; idx < m_sinks.length (); ++idx)
64 : : {
65 : 1191028 : auto sink_ = m_sinks[idx];
66 : 1191028 : auto per_sink_buffer = (*buffer_->m_per_sink_buffers)[idx];
67 : 1191028 : sink_->set_buffer (per_sink_buffer);
68 : : }
69 : : }
70 : : else
71 : 4764080 : for (auto sink_ : m_sinks)
72 : 1191020 : sink_->set_buffer (nullptr);
73 : : }
74 : :
75 : : /* Clear BUFFER_ without flushing it. */
76 : :
77 : : void
78 : 22829483 : context::clear_diagnostic_buffer (buffer &buffer_)
79 : : {
80 : 22829483 : if (buffer_.m_per_sink_buffers)
81 : 19842188 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
82 : 4960547 : per_sink_buffer_->clear ();
83 : :
84 : 22829483 : buffer_.m_diagnostic_counters.clear ();
85 : :
86 : : /* We need to reset last_location, otherwise we may skip caret lines
87 : : when we actually give a diagnostic. */
88 : 22829483 : m_last_location = UNKNOWN_LOCATION;
89 : 22829483 : }
90 : :
91 : : /* Flush the diagnostics in BUFFER_ to this context, clearing BUFFER_. */
92 : :
93 : : void
94 : 7205 : context::flush_diagnostic_buffer (buffer &buffer_)
95 : : {
96 : 7205 : bool had_errors
97 : 7205 : = (buffer_.diagnostic_count (kind::error) > 0
98 : 7205 : || buffer_.diagnostic_count (kind::werror) > 0);
99 : 7205 : if (buffer_.m_per_sink_buffers)
100 : 28820 : for (auto per_sink_buffer_ : *buffer_.m_per_sink_buffers)
101 : 7205 : per_sink_buffer_->flush ();
102 : 7205 : buffer_.m_diagnostic_counters.move_to (m_diagnostic_counters);
103 : :
104 : 11260 : action_after_output (had_errors ? kind::error : kind::warning);
105 : 7205 : check_max_errors (true);
106 : 7202 : }
107 : :
108 : : /* class diagnostics::buffer. */
109 : :
110 : 8386525 : buffer::buffer (context &ctxt)
111 : 8386525 : : m_ctxt (ctxt),
112 : 8386525 : m_per_sink_buffers (nullptr)
113 : : {
114 : 8386525 : }
115 : :
116 : 8355625 : buffer::~buffer ()
117 : : {
118 : 8355625 : if (m_per_sink_buffers)
119 : : {
120 : 376952 : for (auto iter : *m_per_sink_buffers)
121 : 94238 : delete iter;
122 : 188476 : delete m_per_sink_buffers;
123 : : }
124 : 8355625 : }
125 : :
126 : : void
127 : 0 : buffer::dump (FILE *out, int indent) const
128 : : {
129 : 0 : m_diagnostic_counters.dump (out, indent + 2);
130 : 0 : dumping::emit_heading (out, indent, "m_per_sink_buffers");
131 : 0 : if (m_per_sink_buffers)
132 : 0 : for (auto per_sink_buffer_ : *m_per_sink_buffers)
133 : 0 : per_sink_buffer_->dump (out, indent + 2);
134 : : else
135 : 0 : dumping::emit_none (out, indent + 2);
136 : 0 : }
137 : :
138 : : bool
139 : 5993075 : buffer::empty_p () const
140 : : {
141 : 5993075 : if (m_per_sink_buffers)
142 : 9555195 : for (auto per_sink_buffer_ : *m_per_sink_buffers)
143 : : /* Query initial buffer. */
144 : 3185065 : return per_sink_buffer_->empty_p ();
145 : : return true;
146 : : }
147 : :
148 : : void
149 : 103239 : buffer::move_to (buffer &dest)
150 : : {
151 : : /* Bail if there's nothing to move. */
152 : 103239 : if (!m_per_sink_buffers)
153 : : return;
154 : :
155 : 103239 : m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
156 : :
157 : 103239 : if (!dest.m_per_sink_buffers)
158 : : {
159 : : /* Optimization for the "move to empty" case:
160 : : simply move the vec to the dest. */
161 : 80859 : dest.m_per_sink_buffers = m_per_sink_buffers;
162 : 80859 : m_per_sink_buffers = nullptr;
163 : 80859 : return;
164 : : }
165 : :
166 : 22380 : dest.ensure_per_sink_buffers ();
167 : 22380 : gcc_assert (m_per_sink_buffers);
168 : 67140 : gcc_assert (m_per_sink_buffers->length ()
169 : : == m_ctxt.m_sinks.length ());
170 : 22380 : gcc_assert (dest.m_per_sink_buffers);
171 : 44760 : gcc_assert (dest.m_per_sink_buffers->length ()
172 : : == m_ctxt.m_sinks.length ());
173 : 44760 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
174 : : {
175 : 22380 : auto per_sink_buffer_src = (*m_per_sink_buffers)[idx];
176 : 22380 : auto per_sink_buffer_dest = (*dest.m_per_sink_buffers)[idx];
177 : 22380 : per_sink_buffer_src->move_to (*per_sink_buffer_dest);
178 : : }
179 : : }
180 : :
181 : : /* Lazily get the output formats to create their own kind of buffers.
182 : : We can't change the output sinks on a context once this has been called
183 : : on any diagnostics::buffer instances for that context, since there's no
184 : : way to update all diagnostics::buffer instances for that context. */
185 : :
186 : : void
187 : 1213408 : buffer::ensure_per_sink_buffers ()
188 : : {
189 : 1213408 : if (!m_per_sink_buffers)
190 : : {
191 : 96227 : m_per_sink_buffers = new auto_vec<per_sink_buffer *> ();
192 : 192454 : for (unsigned idx = 0; idx < m_ctxt.m_sinks.length (); ++idx)
193 : : {
194 : 96227 : auto sink_ = m_ctxt.m_sinks[idx];
195 : 96227 : auto per_sink_buffer = sink_->make_per_sink_buffer ();
196 : 96227 : m_per_sink_buffers->safe_push (per_sink_buffer.release ());
197 : 96227 : }
198 : : }
199 : 1213408 : gcc_assert (m_per_sink_buffers);
200 : 3640224 : gcc_assert (m_per_sink_buffers->length ()
201 : : == m_ctxt.m_sinks.length ());
202 : 1213408 : }
203 : :
204 : : } // namespace diagnostics
|