Skip to content

Commit f565063

Browse files
committed
diagnostics: implement buffering for non-textual formats [PR105916]
PR fortran/105916 reports stray diagnostics appearing in JSON and SARIF output from gfortran. In order to handle various awkard parsing issues, the Fortran frontend implements buffering of diagnostics, so that diagnostics reported to global_dc can be either: (a) immediately issued, or (b) speculatively reported to global_dc, and stored in a buffer, to either be issued later or discarded. This buffering code in gcc/fortran/error.cc directly manipulates implementation details of the diagnostic_context such as the pretty_printer's buffer, and the counts of how many diagnostics have been issued. The issue is that this manipulation of pretty_printer's buffer doesn't work for formats such as JSON and SARIF where diagnostics are handled in a different way (such as by accumulating json::object instances in an array). This patch moves responsibility for such buffering of diagnostics from fortran's error.cc to the diagnostic subsystem. It introduces a new class diagnostic_buffer representing a particular buffer of diagnostics that have been reported but not yet issued. Each diagnostic output format implements buffering in a different way, and so there is a new class hierarchy, diagnostic_per_format_buffer, representing the various format-specific ways that buffering is to be implemented. This is hidden as an implementation detail of diagnostic_buffer. The patch also updates how diagnostics of each kind (e.g. warnings vs errors) are counted, so that if buffering is enabled, the count is incremented within the buffer, and the counts in the diagnostic_context are only updated if and when the buffer is flushed; checking for max_errors is similarly updated to support both buffered and unbuffered cases. For ease of debugging, the patch extends the "dump" functions within the diagnostics subsystem, so that e.g. global_dc->dump () now prints the buffering status, e.g.: (gdb) call global_dc->dump() diagnostic_context: counts: (none) output format: sarif_output_format printer: m_show_color: false m_url_format: bel m_buffer: m_formatted_obstack current object: length 0: m_chunk_obstack current object: length 0: diagnostic buffer: m_per_format_buffer: counts: error: 1 diagnostic_sarif_format_buffer: result[0]: {"ruleId": "error", "level": "error", "message": {"text": "Function ‘program’ requires an argument list at (1)"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "../../src/gcc/testsuite/gfortran.dg/pr105954.f90", "uriBaseId": "PWD"}, "region": {"startLine": 6, "startColumn": 8, "endColumn": 9}, "contextRegion": {"startLine": 6, "snippet": {"text": "program p\n"}}}}]} which shows that no diagnostics have been issued yet, but the active diagnostic_buffer has a single error buffered within it, in SARIF form. Similarly, it's possible to use "dump" on a diagnostic_buffer to directly query its contents; here's the same example, this time with the text output format: (gdb) call error_buffer.buffer.dump() m_per_format_buffer: counts: error: 1 diagnostic_text_format_buffer: m_formatted_obstack current object: length 232: 00000000: 1b 5b 30 31 6d 1b 5b 4b 2e 2e 2f 2e 2e 2f 73 72 | .[01m.[K../../sr 00000010: 63 2f 67 63 63 2f 74 65 73 74 73 75 69 74 65 2f | c/gcc/testsuite/ 00000020: 67 66 6f 72 74 72 61 6e 2e 64 67 2f 70 72 31 30 | gfortran.dg/pr10 00000030: 35 39 35 34 2e 66 39 30 3a 36 3a 38 3a 1b 5b 6d | 5954.f90:6:8:.[m 00000040: 1b 5b 4b 0a 0a 20 20 20 20 36 20 7c 20 70 72 6f | .[K.. 6 | pro 00000050: 67 72 61 6d 20 70 0a 20 20 20 20 20 20 7c 20 20 | gram p. | 00000060: 20 20 20 20 20 20 1b 5b 30 31 3b 33 31 6d 1b 5b | .[01;31m.[ 00000070: 4b 31 1b 5b 6d 1b 5b 4b 0a 1b 5b 30 31 3b 33 31 | K1.[m.[K..[01;31 00000080: 6d 1b 5b 4b 45 72 72 6f 72 3a 1b 5b 6d 1b 5b 4b | m.[KError:.[m.[K 00000090: 20 46 75 6e 63 74 69 6f 6e 20 e2 80 98 1b 5b 30 | Function ....[0 000000a0: 31 6d 1b 5b 4b 70 72 6f 67 72 61 6d 1b 5b 6d 1b | 1m.[Kprogram.[m. 000000b0: 5b 4b e2 80 99 20 72 65 71 75 69 72 65 73 20 61 | [K... requires a 000000c0: 6e 20 61 72 67 75 6d 65 6e 74 20 6c 69 73 74 20 | n argument list 000000d0: 61 74 20 1b 5b 30 31 3b 33 31 6d 1b 5b 4b 28 31 | at .[01;31m.[K(1 000000e0: 29 1b 5b 6d 1b 5b 4b 0a | ).[m.[K. m_chunk_obstack current object: length 0: showing that we have an error in error_buffer, with colorized text. gcc/ChangeLog: PR fortran/105916 * diagnostic-buffer.h: New file. * diagnostic-format-json.cc: Define INCLUDE_VECTOR. Include "diagnostic-buffer.h". (class diagnostic_json_format_buffer): New subclass. (class json_output_format): Add friend class diagnostic_json_format_buffer. (json_output_format::make_per_format_buffer): New vfunc implementation. (json_output_format::set_buffer): New vfunc implementation. (json_output_format::json_output_format): Initialize m_buffer. (json_output_format::m_buffer): New field. (diagnostic_json_format_buffer::dump): New. (diagnostic_json_format_buffer::empty_p): New. (diagnostic_json_format_buffer::move_to): New. (diagnostic_json_format_buffer::clear): New. (diagnostic_json_format_buffer::flush): New. (json_output_format::on_report_diagnostic): Implement optional buffering. * diagnostic-format-sarif.cc: Include "diagnostic-buffer.h". (class diagnostic_sarif_format_buffer): New subclass. (class sarif_builder): Add friend class diagnostic_sarif_format_buffer. (sarif_builder::num_results): New accessor. (sarif_builder::get_result): New accessor. (sarif_builder::on_report_diagnostic): Add param "buffer"; use it to implement optional buffering. (diagnostic_sarif_format_buffer::dump): New. (diagnostic_sarif_format_buffer::empty_p): New. (diagnostic_sarif_format_buffer::move_to): New. (diagnostic_sarif_format_buffer::clear): New. (diagnostic_sarif_format_buffer::flush): New. (sarif_output_format::make_per_format_buffer): New vfunc implementation. (sarif_output_format::set_buffer): New vfunc implementation. (sarif_output_format::on_report_diagnostic): Pass m_buffer to sarif_builder::on_report_diagnostic. (sarif_output_format::num_results): New accessor. (sarif_output_format::get_result): New accessor. (diagnostic_output_format::diagnostic_output_format): Initialize m_buffer. (diagnostic_output_format::m_buffer): New field. (diagnostic_output_format::num_results): Get accessor. (diagnostic_output_format::get_result): Get accessor. (selftest::get_message_from_result): New. (selftest::test_buffering): New. (selftest::diagnostic_format_sarif_cc_tests): Call it. * diagnostic-format-text.cc: Include "diagnostic-client-data-hooks.h". (class diagnostic_text_format_buffer): New subclass. (diagnostic_text_format_buffer::diagnostic_text_format_buffer): New. (diagnostic_text_format_buffer::dump): New. (diagnostic_text_format_buffer::empty_p): New. (diagnostic_text_format_buffer::move_to): New. (diagnostic_text_format_buffer::clear): New. (diagnostic_text_format_buffer::flush): New. (diagnostic_text_output_format::dump): Dump m_saved_output_buffer. (diagnostic_text_output_format::set_buffer): New. (diagnostic_text_output_format::make_per_format_buffer): New. * diagnostic-format-text.h (diagnostic_text_output_format::diagnostic_text_output_format): Initialize m_saved_output_buffer. (diagnostic_text_output_format::set_buffer): New decl. (diagnostic_text_output_format::make_per_format_buffer): New decl. (diagnostic_text_output_format::m_saved_output_buffer): New field. * diagnostic-format.h (class diagnostic_per_format_buffer): New forward decl. (diagnostic_output_format::make_per_format_buffer): New vfunc. (diagnostic_output_format::set_buffer): New vfunc. * diagnostic.cc: Include "diagnostic-buffer.h". (diagnostic_context::initialize): Replace memset with call to "clear" on m_diagnostic_counters. Initializer m_diagnostic_buffer. (diagnostic_context::finish): Call set_diagnostic_buffer with nullptr. (diagnostic_context::dump): Update for encapsulation of counts into m_diagnostic_counters. Dump m_diagnostic_buffer. (diagnostic_context::execution_failed_p): Update for encapsulation of counts into m_diagnostic_counters. (diagnostic_context::check_max_errors): Likewise. (diagnostic_context::report_diagnostic): Likewise. Eliminate diagnostic_check_max_errors in favor of check_max_errors. Update increment of counter to support buffering. Eliminate diagnostic_action_after_output in favor of action_after_output. Only add fixits to m_edit_context_ptr if buffering is disabled. Only call diagnostic_output_format::after_diagnostic if buffering is disabled. (diagnostic_context::error_recursion): Eliminate diagnostic_action_after_output in favor of action_after_output. (diagnostic_context::set_diagnostic_buffer): New. (diagnostic_context::clear_diagnostic_buffer): New. (diagnostic_context::flush_diagnostic_buffer): New. (diagnostic_counters::diagnostic_counters): New. (diagnostic_counters::dump): New. (diagnostic_counters::move_to): New. (diagnostic_counters::clear): New. (diagnostic_buffer::diagnostic_buffer): New. (diagnostic_buffer::~diagnostic_buffer): New. (diagnostic_buffer::dump): New. (diagnostic_buffer::empty_p): New. (diagnostic_buffer::move_to): New. (diagnostic_buffer::ensure_per_format_buffer): New. (c_diagnostic_cc_tests): Remove stray newline. * diagnostic.h (class diagnostic_buffer): New forward decl. (struct diagnostic_counters): New. (diagnostic_context::check_max_errors): Make private. (diagnostic_context::action_after_output): Make private. (diagnostic_context::get_output_format): Make non-const. (diagnostic_context::diagnostic_count): Update for change to m_diagnostic_counters. (diagnostic_context::set_diagnostic_buffer): New decl. (diagnostic_context::get_diagnostic_buffer): New decl. (diagnostic_context::clear_diagnostic_buffer): New decl. (diagnostic_context::flush_diagnostic_buffer): New decl. (diagnostic_context::m_diagnostic_count): Replace array with... (diagnostic_context::m_diagnostic_counters): ...this. (diagnostic_context::m_diagnostic_buffer): New field. (diagnostic_action_after_output): Delete. (diagnostic_check_max_errors): Delete. gcc/fortran/ChangeLog: PR fortran/105916 * error.cc (pp_error_buffer, pp_warning_buffer): Convert from output_buffer * to diagnostic_buffer *. (warningcount_buffered, werrorcount_buffered): Eliminate. (gfc_error_buffer::gfc_error_buffer): Move constructor definition here, and initialize "buffer" using *global_dc. (gfc_output_buffer_empty_p): Delete in favor of diagnostic_buffer::empty_p. (gfc_clear_pp_buffer): Replace with... (gfc_clear_diagnostic_buffer): ...this, moving implementation details to diagnostic_context::clear_diagnostic_buffer. (gfc_warning): Replace buffering implementation with calls to global_dc->get_diagnostic_buffer and global_dc->set_diagnostic_buffer. (gfc_clear_warning): Update for renaming of gfc_clear_pp_buffer and elimination of warningcount_buffered and werrorcount_buffered. (gfc_warning_check): Replace buffering implementation with calls to pp_warning_buffer->empty_p and global_dc->flush_diagnostic_buffer. (gfc_error_opt): Replace buffering implementation with calls to global_dc->get_diagnostic_buffer and set_diagnostic_buffer. (gfc_clear_error): Update for renaming of gfc_clear_pp_buffer. (gfc_error_flag_test): Replace call to gfc_output_buffer_empty_p with call to diagnostic_buffer::empty_p. (gfc_error_check): Replace buffering implementation with calls to pp_error_buffer->empty_p and global_dc->flush_diagnostic_buffer. (gfc_move_error_buffer_from_to): Replace buffering implementation with usage of diagnostic_buffer. (gfc_free_error): Update for renaming of gfc_clear_pp_buffer. (gfc_diagnostics_init): Use "new" directly when creating pp_warning_buffer. Remove setting of m_flush_p on the two buffers, as this is handled by diagnostic_buffer and by diagnostic_text_format_buffer's constructor. * gfortran.h: Replace #include "pretty-print.h" for output_buffer with #include "diagnostic-buffer.h" for diagnostic_buffer. (struct gfc_error_buffer): Change type of field "buffer" from output_buffer to diagnostic_buffer. Move definition of constructor into error.cc so that it can use global_dc. gcc/testsuite/ChangeLog: PR fortran/105916 * gcc.dg/plugin/diagnostic_plugin_xhtml_format.c: Include "diagnostic-buffer.h". (class diagnostic_xhtml_format_buffer): New subclass. (class xhtml_builder): Add friend class diagnostic_xhtml_format_buffer. (diagnostic_xhtml_format_buffer::dump): New. (diagnostic_xhtml_format_buffer::empty_p): New. (diagnostic_xhtml_format_buffer::move_to): New. (diagnostic_xhtml_format_buffer::clear): New. (diagnostic_xhtml_format_buffer::flush): New. (xhtml_builder::on_report_diagnostic): Add "buffer" param, and use it. (xhtml_output_format::dump): Fix typo. (xhtml_output_format::make_per_format_buffer): New. (xhtml_output_format::set_buffer): New. (xhtml_output_format::on_report_diagnostic): Fix whitespace. Pass m_buffer to xhtml_builder::on_report_diagnostic. (xhtml_output_format::xhtml_output_format): Initialize m_buffer. (xhtml_output_format::m_buffer): New field. * gfortran.dg/diagnostic-format-json-pr105916.F90: New test. * gfortran.dg/diagnostic-format-sarif-1.F90: New test. * gfortran.dg/diagnostic-format-sarif-1.py: New support script. * gfortran.dg/diagnostic-format-sarif-pr105916.f90: New test. Signed-off-by: David Malcolm <[email protected]>
1 parent de2dc62 commit f565063

15 files changed

+1069
-191
lines changed

gcc/diagnostic-buffer.h

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* Support for buffering diagnostics before flushing them to output format.
2+
Copyright (C) 2024 Free Software Foundation, Inc.
3+
Contributed by David Malcolm <[email protected]>.
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+
#ifndef GCC_DIAGNOSTIC_BUFFER_H
22+
#define GCC_DIAGNOSTIC_BUFFER_H
23+
24+
#include "diagnostic.h"
25+
26+
class diagnostic_per_format_buffer;
27+
class diagnostic_output_format;
28+
class diagnostic_text_output_format;
29+
30+
/* Class representing a buffer of zero or more diagnostics that
31+
have been reported to a diagnostic_context, but which haven't
32+
yet been flushed.
33+
34+
A diagnostic_buffer can be:
35+
36+
* flushed to the diagnostic_context, which issues
37+
the diagnostics within the buffer to the output format
38+
and checks for limits such as -fmax-errors=, or
39+
40+
* moved to another diagnostic_buffer, which moves the diagnostics
41+
within the first buffer to the other buffer, appending them after any
42+
existing diagnostics within the destination buffer, emptying the
43+
source buffer, or
44+
45+
* cleared, which discards any diagnostics within the buffer
46+
without issuing them to the output format.
47+
48+
Since a buffer needs to contain output-format-specific data,
49+
it's not possible to change the output format of the
50+
diagnostic_context once any buffers are non-empty.
51+
52+
To simplify implementing output formats, it's not possible
53+
to change buffering on a diagnostic_context whilst within a
54+
diagnostic group. */
55+
56+
class diagnostic_buffer
57+
{
58+
public:
59+
friend class diagnostic_context;
60+
61+
diagnostic_buffer (diagnostic_context &ctxt);
62+
~diagnostic_buffer ();
63+
64+
void dump (FILE *out, int indent) const;
65+
void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
66+
67+
int diagnostic_count (diagnostic_t kind) const
68+
{
69+
return m_diagnostic_counters.get_count (kind);
70+
}
71+
72+
bool empty_p () const;
73+
74+
void move_to (diagnostic_buffer &dest);
75+
76+
private:
77+
void ensure_per_format_buffer ();
78+
79+
diagnostic_context &m_ctxt;
80+
diagnostic_per_format_buffer *m_per_format_buffer;
81+
82+
/* The number of buffered diagnostics of each kind. */
83+
diagnostic_counters m_diagnostic_counters;
84+
};
85+
86+
/* Implementation detail of diagnostic_buffer.
87+
88+
Abstract base class describing how to represent zero of more
89+
buffered diagnostics for a particular diagnostic_output_format
90+
(e.g. text vs SARIF).
91+
92+
Each diagnostic_output_format subclass should implement its own
93+
subclass for handling diagnostic_buffer. */
94+
95+
class diagnostic_per_format_buffer
96+
{
97+
public:
98+
virtual ~diagnostic_per_format_buffer () {}
99+
100+
virtual void dump (FILE *out, int indent) const = 0;
101+
void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
102+
103+
virtual bool empty_p () const = 0;
104+
virtual void move_to (diagnostic_per_format_buffer &dest) = 0;
105+
virtual void clear () = 0;
106+
virtual void flush () = 0;
107+
};
108+
109+
#endif /* ! GCC_DIAGNOSTIC_BUFFER_H */

gcc/diagnostic-format-json.cc

+109-14
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,68 @@ along with GCC; see the file COPYING3. If not see
2121

2222
#include "config.h"
2323
#define INCLUDE_MEMORY
24+
#define INCLUDE_VECTOR
2425
#include "system.h"
2526
#include "coretypes.h"
2627
#include "diagnostic.h"
2728
#include "selftest-diagnostic.h"
2829
#include "diagnostic-metadata.h"
2930
#include "diagnostic-path.h"
3031
#include "diagnostic-format.h"
32+
#include "diagnostic-buffer.h"
3133
#include "json.h"
3234
#include "selftest.h"
3335
#include "logical-location.h"
3436
#include "make-unique.h"
3537

38+
class json_output_format;
39+
40+
/* Concrete buffering implementation subclass for JSON output. */
41+
42+
class diagnostic_json_format_buffer : public diagnostic_per_format_buffer
43+
{
44+
public:
45+
friend class json_output_format;
46+
47+
diagnostic_json_format_buffer (json_output_format &format)
48+
: m_format (format)
49+
{}
50+
51+
void dump (FILE *out, int indent) const final override;
52+
bool empty_p () const final override;
53+
void move_to (diagnostic_per_format_buffer &dest) final override;
54+
void clear () final override;
55+
void flush () final override;
56+
57+
private:
58+
json_output_format &m_format;
59+
std::vector<std::unique_ptr<json::object>> m_results;
60+
};
61+
3662
/* Subclass of diagnostic_output_format for JSON output. */
3763

3864
class json_output_format : public diagnostic_output_format
3965
{
4066
public:
67+
friend class diagnostic_json_format_buffer;
68+
4169
void dump (FILE *out, int indent) const override
4270
{
4371
fprintf (out, "%*sjson_output_format\n", indent, "");
4472
diagnostic_output_format::dump (out, indent);
4573
}
4674

75+
diagnostic_per_format_buffer *make_per_format_buffer () final override
76+
{
77+
return new diagnostic_json_format_buffer (*this);
78+
}
79+
void set_buffer (diagnostic_per_format_buffer *base_buffer) final override
80+
{
81+
diagnostic_json_format_buffer *buffer
82+
= static_cast<diagnostic_json_format_buffer *> (base_buffer);
83+
m_buffer = buffer;
84+
}
85+
4786
void on_begin_group () final override
4887
{
4988
/* No-op. */
@@ -69,6 +108,7 @@ class json_output_format : public diagnostic_output_format
69108
json_output_format (diagnostic_context &context,
70109
bool formatted)
71110
: diagnostic_output_format (context),
111+
m_buffer (nullptr),
72112
m_toplevel_array (::make_unique<json::array> ()),
73113
m_cur_group (nullptr),
74114
m_cur_children_array (nullptr),
@@ -86,6 +126,8 @@ class json_output_format : public diagnostic_output_format
86126
}
87127

88128
private:
129+
diagnostic_json_format_buffer *m_buffer;
130+
89131
/* The top-level JSON array of pending diagnostics. */
90132
std::unique_ptr<json::array> m_toplevel_array;
91133

@@ -231,6 +273,51 @@ make_json_for_path (diagnostic_context &context,
231273
return path_array;
232274
}
233275

276+
/* class diagnostic_json_format_buffer : public diagnostic_per_format_buffer. */
277+
278+
void
279+
diagnostic_json_format_buffer::dump (FILE *out, int indent) const
280+
{
281+
fprintf (out, "%*sdiagnostic_json_format_buffer:\n", indent, "");
282+
int idx = 0;
283+
for (auto &result : m_results)
284+
{
285+
fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx);
286+
result->dump (out, true);
287+
fprintf (out, "\n");
288+
++idx;
289+
}
290+
}
291+
292+
bool
293+
diagnostic_json_format_buffer::empty_p () const
294+
{
295+
return m_results.empty ();
296+
}
297+
298+
void
299+
diagnostic_json_format_buffer::move_to (diagnostic_per_format_buffer &base)
300+
{
301+
diagnostic_json_format_buffer &dest
302+
= static_cast<diagnostic_json_format_buffer &> (base);
303+
for (auto &&result : m_results)
304+
dest.m_results.push_back (std::move (result));
305+
m_results.clear ();
306+
}
307+
308+
void
309+
diagnostic_json_format_buffer::clear ()
310+
{
311+
m_results.clear ();
312+
}
313+
314+
void
315+
diagnostic_json_format_buffer::flush ()
316+
{
317+
for (auto &&result : m_results)
318+
m_format.m_toplevel_array->append (std::move (result));
319+
m_results.clear ();
320+
}
234321

235322
/* Implementation of "on_report_diagnostic" vfunc for JSON output.
236323
Generate a JSON object for DIAGNOSTIC, and store for output
@@ -277,24 +364,32 @@ json_output_format::on_report_diagnostic (const diagnostic_info &diagnostic,
277364
free (option_url);
278365
}
279366

280-
/* If we've already emitted a diagnostic within this auto_diagnostic_group,
281-
then add diag_obj to its "children" array. */
282-
if (m_cur_group)
367+
if (m_buffer)
283368
{
284-
gcc_assert (m_cur_children_array);
285-
m_cur_children_array->append (diag_obj);
369+
gcc_assert (!m_cur_group);
370+
m_buffer->m_results.push_back (std::unique_ptr<json::object> (diag_obj));
286371
}
287372
else
288373
{
289-
/* Otherwise, make diag_obj be the top-level object within the group;
290-
add a "children" array and record the column origin. */
291-
m_cur_group = diag_obj;
292-
std::unique_ptr<json::array> children_array
293-
= ::make_unique<json::array> ();
294-
m_cur_children_array = children_array.get (); // borrowed
295-
diag_obj->set ("children", std::move (children_array));
296-
diag_obj->set_integer ("column-origin", m_context.m_column_origin);
297-
m_toplevel_array->append (diag_obj);
374+
/* If we've already emitted a diagnostic within this auto_diagnostic_group,
375+
then add diag_obj to its "children" array. */
376+
if (m_cur_group)
377+
{
378+
gcc_assert (m_cur_children_array);
379+
m_cur_children_array->append (diag_obj);
380+
}
381+
else
382+
{
383+
/* Otherwise, make diag_obj be the top-level object within the group;
384+
add a "children" array and record the column origin. */
385+
m_cur_group = diag_obj;
386+
std::unique_ptr<json::array> children_array
387+
= ::make_unique<json::array> ();
388+
m_cur_children_array = children_array.get (); // borrowed
389+
diag_obj->set ("children", std::move (children_array));
390+
diag_obj->set_integer ("column-origin", m_context.m_column_origin);
391+
m_toplevel_array->append (diag_obj);
392+
}
298393
}
299394

300395
/* diag_obj is now owned by either m_cur_children_array or

0 commit comments

Comments
 (0)