1 /* JSON output for diagnostics
2 Copyright (C) 2018-2020 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"
26 #include "diagnostic-metadata.h"
30 /* The top-level JSON array of pending diagnostics. */
32 static json::array
*toplevel_array
;
34 /* The JSON object for the current diagnostic group. */
36 static json::object
*cur_group
;
38 /* The JSON array for the "children" array within the current diagnostic
41 static json::array
*cur_children_array
;
43 /* Generate a JSON object for LOC. */
46 json_from_expanded_location (location_t loc
)
48 expanded_location exploc
= expand_location (loc
);
49 json::object
*result
= new json::object ();
51 result
->set ("file", new json::string (exploc
.file
));
52 result
->set ("line", new json::integer_number (exploc
.line
));
53 result
->set ("column", new json::integer_number (exploc
.column
));
57 /* Generate a JSON object for LOC_RANGE. */
60 json_from_location_range (const location_range
*loc_range
, unsigned range_idx
)
62 location_t caret_loc
= get_pure_location (loc_range
->m_loc
);
64 if (caret_loc
== UNKNOWN_LOCATION
)
67 location_t start_loc
= get_start (loc_range
->m_loc
);
68 location_t finish_loc
= get_finish (loc_range
->m_loc
);
70 json::object
*result
= new json::object ();
71 result
->set ("caret", json_from_expanded_location (caret_loc
));
72 if (start_loc
!= caret_loc
73 && start_loc
!= UNKNOWN_LOCATION
)
74 result
->set ("start", json_from_expanded_location (start_loc
));
75 if (finish_loc
!= caret_loc
76 && finish_loc
!= UNKNOWN_LOCATION
)
77 result
->set ("finish", json_from_expanded_location (finish_loc
));
79 if (loc_range
->m_label
)
82 text
= loc_range
->m_label
->get_text (range_idx
);
84 result
->set ("label", new json::string (text
.m_buffer
));
91 /* Generate a JSON object for HINT. */
94 json_from_fixit_hint (const fixit_hint
*hint
)
96 json::object
*fixit_obj
= new json::object ();
98 location_t start_loc
= hint
->get_start_loc ();
99 fixit_obj
->set ("start", json_from_expanded_location (start_loc
));
100 location_t next_loc
= hint
->get_next_loc ();
101 fixit_obj
->set ("next", json_from_expanded_location (next_loc
));
102 fixit_obj
->set ("string", new json::string (hint
->get_string ()));
107 /* Generate a JSON object for METADATA. */
109 static json::object
*
110 json_from_metadata (const diagnostic_metadata
*metadata
)
112 json::object
*metadata_obj
= new json::object ();
114 if (metadata
->get_cwe ())
115 metadata_obj
->set ("cwe",
116 new json::integer_number (metadata
->get_cwe ()));
121 /* No-op implementation of "begin_diagnostic" for JSON output. */
124 json_begin_diagnostic (diagnostic_context
*, diagnostic_info
*)
128 /* Implementation of "end_diagnostic" for JSON output.
129 Generate a JSON object for DIAGNOSTIC, and store for output
130 within current diagnostic group. */
133 json_end_diagnostic (diagnostic_context
*context
, diagnostic_info
*diagnostic
,
134 diagnostic_t orig_diag_kind
)
136 json::object
*diag_obj
= new json::object ();
138 /* Get "kind" of diagnostic. */
140 static const char *const diagnostic_kind_text
[] = {
141 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
142 #include "diagnostic.def"
143 #undef DEFINE_DIAGNOSTIC_KIND
146 /* Lose the trailing ": ". */
147 const char *kind_text
= diagnostic_kind_text
[diagnostic
->kind
];
148 size_t len
= strlen (kind_text
);
149 gcc_assert (len
> 2);
150 gcc_assert (kind_text
[len
- 2] == ':');
151 gcc_assert (kind_text
[len
- 1] == ' ');
152 char *rstrip
= xstrdup (kind_text
);
153 rstrip
[len
- 2] = '\0';
154 diag_obj
->set ("kind", new json::string (rstrip
));
158 // FIXME: encoding of the message (json::string requires UTF-8)
159 diag_obj
->set ("message",
160 new json::string (pp_formatted_text (context
->printer
)));
161 pp_clear_output_area (context
->printer
);
164 option_text
= context
->option_name (context
, diagnostic
->option_index
,
165 orig_diag_kind
, diagnostic
->kind
);
168 diag_obj
->set ("option", new json::string (option_text
));
172 if (context
->get_option_url
)
174 char *option_url
= context
->get_option_url (context
,
175 diagnostic
->option_index
);
178 diag_obj
->set ("option_url", new json::string (option_url
));
183 /* If we've already emitted a diagnostic within this auto_diagnostic_group,
184 then add diag_obj to its "children" array. */
187 gcc_assert (cur_children_array
);
188 cur_children_array
->append (diag_obj
);
192 /* Otherwise, make diag_obj be the top-level object within the group;
193 add a "children" array. */
194 toplevel_array
->append (diag_obj
);
195 cur_group
= diag_obj
;
196 cur_children_array
= new json::array ();
197 diag_obj
->set ("children", cur_children_array
);
200 const rich_location
*richloc
= diagnostic
->richloc
;
202 json::array
*loc_array
= new json::array ();
203 diag_obj
->set ("locations", loc_array
);
205 for (unsigned int i
= 0; i
< richloc
->get_num_locations (); i
++)
207 const location_range
*loc_range
= richloc
->get_range (i
);
208 json::object
*loc_obj
= json_from_location_range (loc_range
, i
);
210 loc_array
->append (loc_obj
);
213 if (richloc
->get_num_fixit_hints ())
215 json::array
*fixit_array
= new json::array ();
216 diag_obj
->set ("fixits", fixit_array
);
217 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
219 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
220 json::object
*fixit_obj
= json_from_fixit_hint (hint
);
221 fixit_array
->append (fixit_obj
);
225 /* TODO: tree-ish things:
227 TODO: inlining information
228 TODO: macro expansion information. */
230 if (diagnostic
->metadata
)
232 json::object
*metadata_obj
= json_from_metadata (diagnostic
->metadata
);
233 diag_obj
->set ("metadata", metadata_obj
);
236 const diagnostic_path
*path
= richloc
->get_path ();
237 if (path
&& context
->make_json_for_path
)
239 json::value
*path_value
= context
->make_json_for_path (context
, path
);
240 diag_obj
->set ("path", path_value
);
244 /* No-op implementation of "begin_group_cb" for JSON output. */
247 json_begin_group (diagnostic_context
*)
251 /* Implementation of "end_group_cb" for JSON output. */
254 json_end_group (diagnostic_context
*)
257 cur_children_array
= NULL
;
260 /* Callback for final cleanup for JSON output. */
263 json_final_cb (diagnostic_context
*)
265 /* Flush the top-level array. */
266 toplevel_array
->dump (stderr
);
267 fprintf (stderr
, "\n");
268 delete toplevel_array
;
269 toplevel_array
= NULL
;
272 /* Set the output format for CONTEXT to FORMAT. */
275 diagnostic_output_format_init (diagnostic_context
*context
,
276 enum diagnostics_output_format format
)
282 case DIAGNOSTICS_OUTPUT_FORMAT_TEXT
:
283 /* The default; do nothing. */
286 case DIAGNOSTICS_OUTPUT_FORMAT_JSON
:
288 /* Set up top-level JSON array. */
289 if (toplevel_array
== NULL
)
290 toplevel_array
= new json::array ();
292 /* Override callbacks. */
293 context
->begin_diagnostic
= json_begin_diagnostic
;
294 context
->end_diagnostic
= json_end_diagnostic
;
295 context
->begin_group_cb
= json_begin_group
;
296 context
->end_group_cb
= json_end_group
;
297 context
->final_cb
= json_final_cb
;
298 context
->print_path
= NULL
; /* handled in json_end_diagnostic. */
300 /* The metadata is handled in JSON format, rather than as text. */
301 context
->show_cwe
= false;
303 /* The option is handled in JSON format, rather than as text. */
304 context
->show_option_requested
= false;
306 /* Don't colorize the text. */
307 pp_show_color (context
->printer
) = false;
317 /* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
318 but verify that we handle this gracefully. */
321 test_unknown_location ()
323 delete json_from_expanded_location (UNKNOWN_LOCATION
);
326 /* Verify that we gracefully handle attempts to serialize bad
327 compound locations. */
330 test_bad_endpoints ()
332 location_t bad_endpoints
333 = make_location (BUILTINS_LOCATION
,
334 UNKNOWN_LOCATION
, UNKNOWN_LOCATION
);
336 location_range loc_range
;
337 loc_range
.m_loc
= bad_endpoints
;
338 loc_range
.m_range_display_kind
= SHOW_RANGE_WITH_CARET
;
339 loc_range
.m_label
= NULL
;
341 json::object
*obj
= json_from_location_range (&loc_range
, 0);
342 /* We should have a "caret" value, but no "start" or "finish" values. */
343 ASSERT_TRUE (obj
!= NULL
);
344 ASSERT_TRUE (obj
->get ("caret") != NULL
);
345 ASSERT_TRUE (obj
->get ("start") == NULL
);
346 ASSERT_TRUE (obj
->get ("finish") == NULL
);
350 /* Run all of the selftests within this file. */
353 diagnostic_format_json_cc_tests ()
355 test_unknown_location ();
356 test_bad_endpoints ();
359 } // namespace selftest
361 #endif /* #if CHECKING_P */