1 /* JSON output for diagnostics
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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
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
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/>. */
24 #include "coretypes.h"
25 #include "diagnostic.h"
29 /* The top-level JSON array of pending diagnostics. */
31 static json::array
*toplevel_array
;
33 /* The JSON object for the current diagnostic group. */
35 static json::object
*cur_group
;
37 /* The JSON array for the "children" array within the current diagnostic
40 static json::array
*cur_children_array
;
42 /* Generate a JSON object for LOC. */
45 json_from_expanded_location (location_t loc
)
47 expanded_location exploc
= expand_location (loc
);
48 json::object
*result
= new json::object ();
50 result
->set ("file", new json::string (exploc
.file
));
51 result
->set ("line", new json::number (exploc
.line
));
52 result
->set ("column", new json::number (exploc
.column
));
56 /* Generate a JSON object for LOC_RANGE. */
59 json_from_location_range (const location_range
*loc_range
, unsigned range_idx
)
61 location_t caret_loc
= get_pure_location (loc_range
->m_loc
);
63 if (caret_loc
== UNKNOWN_LOCATION
)
66 location_t start_loc
= get_start (loc_range
->m_loc
);
67 location_t finish_loc
= get_finish (loc_range
->m_loc
);
69 json::object
*result
= new json::object ();
70 result
->set ("caret", json_from_expanded_location (caret_loc
));
71 if (start_loc
!= caret_loc
72 && start_loc
!= UNKNOWN_LOCATION
)
73 result
->set ("start", json_from_expanded_location (start_loc
));
74 if (finish_loc
!= caret_loc
75 && finish_loc
!= UNKNOWN_LOCATION
)
76 result
->set ("finish", json_from_expanded_location (finish_loc
));
78 if (loc_range
->m_label
)
81 text
= loc_range
->m_label
->get_text (range_idx
);
83 result
->set ("label", new json::string (text
.m_buffer
));
90 /* Generate a JSON object for HINT. */
93 json_from_fixit_hint (const fixit_hint
*hint
)
95 json::object
*fixit_obj
= new json::object ();
97 location_t start_loc
= hint
->get_start_loc ();
98 fixit_obj
->set ("start", json_from_expanded_location (start_loc
));
99 location_t next_loc
= hint
->get_next_loc ();
100 fixit_obj
->set ("next", json_from_expanded_location (next_loc
));
101 fixit_obj
->set ("string", new json::string (hint
->get_string ()));
106 /* No-op implementation of "begin_diagnostic" for JSON output. */
109 json_begin_diagnostic (diagnostic_context
*, diagnostic_info
*)
113 /* Implementation of "end_diagnostic" for JSON output.
114 Generate a JSON object for DIAGNOSTIC, and store for output
115 within current diagnostic group. */
118 json_end_diagnostic (diagnostic_context
*context
, diagnostic_info
*diagnostic
,
119 diagnostic_t orig_diag_kind
)
121 json::object
*diag_obj
= new json::object ();
123 /* Get "kind" of diagnostic. */
125 static const char *const diagnostic_kind_text
[] = {
126 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
127 #include "diagnostic.def"
128 #undef DEFINE_DIAGNOSTIC_KIND
131 /* Lose the trailing ": ". */
132 const char *kind_text
= diagnostic_kind_text
[diagnostic
->kind
];
133 size_t len
= strlen (kind_text
);
134 gcc_assert (len
> 2);
135 gcc_assert (kind_text
[len
- 2] == ':');
136 gcc_assert (kind_text
[len
- 1] == ' ');
137 char *rstrip
= xstrdup (kind_text
);
138 rstrip
[len
- 2] = '\0';
139 diag_obj
->set ("kind", new json::string (rstrip
));
143 // FIXME: encoding of the message (json::string requires UTF-8)
144 diag_obj
->set ("message",
145 new json::string (pp_formatted_text (context
->printer
)));
146 pp_clear_output_area (context
->printer
);
149 option_text
= context
->option_name (context
, diagnostic
->option_index
,
150 orig_diag_kind
, diagnostic
->kind
);
153 diag_obj
->set ("option", new json::string (option_text
));
157 /* If we've already emitted a diagnostic within this auto_diagnostic_group,
158 then add diag_obj to its "children" array. */
161 gcc_assert (cur_children_array
);
162 cur_children_array
->append (diag_obj
);
166 /* Otherwise, make diag_obj be the top-level object within the group;
167 add a "children" array. */
168 toplevel_array
->append (diag_obj
);
169 cur_group
= diag_obj
;
170 cur_children_array
= new json::array ();
171 diag_obj
->set ("children", cur_children_array
);
174 const rich_location
*richloc
= diagnostic
->richloc
;
176 json::array
*loc_array
= new json::array ();
177 diag_obj
->set ("locations", loc_array
);
179 for (unsigned int i
= 0; i
< richloc
->get_num_locations (); i
++)
181 const location_range
*loc_range
= richloc
->get_range (i
);
182 json::object
*loc_obj
= json_from_location_range (loc_range
, i
);
184 loc_array
->append (loc_obj
);
187 if (richloc
->get_num_fixit_hints ())
189 json::array
*fixit_array
= new json::array ();
190 diag_obj
->set ("fixits", fixit_array
);
191 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
193 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
194 json::object
*fixit_obj
= json_from_fixit_hint (hint
);
195 fixit_array
->append (fixit_obj
);
199 /* TODO: tree-ish things:
201 TODO: inlining information
202 TODO: macro expansion information. */
205 /* No-op implementation of "begin_group_cb" for JSON output. */
208 json_begin_group (diagnostic_context
*)
212 /* Implementation of "end_group_cb" for JSON output. */
215 json_end_group (diagnostic_context
*)
218 cur_children_array
= NULL
;
221 /* Callback for final cleanup for JSON output. */
224 json_final_cb (diagnostic_context
*)
226 /* Flush the top-level array. */
227 toplevel_array
->dump (stderr
);
228 fprintf (stderr
, "\n");
229 delete toplevel_array
;
230 toplevel_array
= NULL
;
233 /* Set the output format for CONTEXT to FORMAT. */
236 diagnostic_output_format_init (diagnostic_context
*context
,
237 enum diagnostics_output_format format
)
243 case DIAGNOSTICS_OUTPUT_FORMAT_TEXT
:
244 /* The default; do nothing. */
247 case DIAGNOSTICS_OUTPUT_FORMAT_JSON
:
249 /* Set up top-level JSON array. */
250 if (toplevel_array
== NULL
)
251 toplevel_array
= new json::array ();
253 /* Override callbacks. */
254 context
->begin_diagnostic
= json_begin_diagnostic
;
255 context
->end_diagnostic
= json_end_diagnostic
;
256 context
->begin_group_cb
= json_begin_group
;
257 context
->end_group_cb
= json_end_group
;
258 context
->final_cb
= json_final_cb
;
260 /* The option is handled in JSON format, rather than as text. */
261 context
->show_option_requested
= false;
263 /* Don't colorize the text. */
264 pp_show_color (context
->printer
) = false;
274 /* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
275 but verify that we handle this gracefully. */
278 test_unknown_location ()
280 delete json_from_expanded_location (UNKNOWN_LOCATION
);
283 /* Verify that we gracefully handle attempts to serialize bad
284 compound locations. */
287 test_bad_endpoints ()
289 location_t bad_endpoints
290 = make_location (BUILTINS_LOCATION
,
291 UNKNOWN_LOCATION
, UNKNOWN_LOCATION
);
293 location_range loc_range
;
294 loc_range
.m_loc
= bad_endpoints
;
295 loc_range
.m_range_display_kind
= SHOW_RANGE_WITH_CARET
;
296 loc_range
.m_label
= NULL
;
298 json::object
*obj
= json_from_location_range (&loc_range
, 0);
299 /* We should have a "caret" value, but no "start" or "finish" values. */
300 ASSERT_TRUE (obj
!= NULL
);
301 ASSERT_TRUE (obj
->get ("caret") != NULL
);
302 ASSERT_TRUE (obj
->get ("start") == NULL
);
303 ASSERT_TRUE (obj
->get ("finish") == NULL
);
307 /* Run all of the selftests within this file. */
310 diagnostic_format_json_cc_tests ()
312 test_unknown_location ();
313 test_bad_endpoints ();
316 } // namespace selftest
318 #endif /* #if CHECKING_P */