1 /* JSON output for diagnostics
2 Copyright (C) 2018-2021 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 "selftest-diagnostic.h"
27 #include "diagnostic-metadata.h"
31 /* The top-level JSON array of pending diagnostics. */
33 static json::array
*toplevel_array
;
35 /* The JSON object for the current diagnostic group. */
37 static json::object
*cur_group
;
39 /* The JSON array for the "children" array within the current diagnostic
42 static json::array
*cur_children_array
;
44 /* Generate a JSON object for LOC. */
47 json_from_expanded_location (diagnostic_context
*context
, location_t loc
)
49 expanded_location exploc
= expand_location (loc
);
50 json::object
*result
= new json::object ();
52 result
->set ("file", new json::string (exploc
.file
));
53 result
->set ("line", new json::integer_number (exploc
.line
));
55 const enum diagnostics_column_unit orig_unit
= context
->column_unit
;
59 enum diagnostics_column_unit unit
;
61 {"display-column", DIAGNOSTICS_COLUMN_UNIT_DISPLAY
},
62 {"byte-column", DIAGNOSTICS_COLUMN_UNIT_BYTE
}
64 int the_column
= INT_MIN
;
65 for (int i
= 0; i
!= sizeof column_fields
/ sizeof (*column_fields
); ++i
)
67 context
->column_unit
= column_fields
[i
].unit
;
68 const int col
= diagnostic_converted_column (context
, exploc
);
69 result
->set (column_fields
[i
].name
, new json::integer_number (col
));
70 if (column_fields
[i
].unit
== orig_unit
)
73 gcc_assert (the_column
!= INT_MIN
);
74 result
->set ("column", new json::integer_number (the_column
));
75 context
->column_unit
= orig_unit
;
79 /* Generate a JSON object for LOC_RANGE. */
82 json_from_location_range (diagnostic_context
*context
,
83 const location_range
*loc_range
, unsigned range_idx
)
85 location_t caret_loc
= get_pure_location (loc_range
->m_loc
);
87 if (caret_loc
== UNKNOWN_LOCATION
)
90 location_t start_loc
= get_start (loc_range
->m_loc
);
91 location_t finish_loc
= get_finish (loc_range
->m_loc
);
93 json::object
*result
= new json::object ();
94 result
->set ("caret", json_from_expanded_location (context
, caret_loc
));
95 if (start_loc
!= caret_loc
96 && start_loc
!= UNKNOWN_LOCATION
)
97 result
->set ("start", json_from_expanded_location (context
, start_loc
));
98 if (finish_loc
!= caret_loc
99 && finish_loc
!= UNKNOWN_LOCATION
)
100 result
->set ("finish", json_from_expanded_location (context
, finish_loc
));
102 if (loc_range
->m_label
)
105 text
= loc_range
->m_label
->get_text (range_idx
);
107 result
->set ("label", new json::string (text
.m_buffer
));
114 /* Generate a JSON object for HINT. */
116 static json::object
*
117 json_from_fixit_hint (diagnostic_context
*context
, const fixit_hint
*hint
)
119 json::object
*fixit_obj
= new json::object ();
121 location_t start_loc
= hint
->get_start_loc ();
122 fixit_obj
->set ("start", json_from_expanded_location (context
, start_loc
));
123 location_t next_loc
= hint
->get_next_loc ();
124 fixit_obj
->set ("next", json_from_expanded_location (context
, next_loc
));
125 fixit_obj
->set ("string", new json::string (hint
->get_string ()));
130 /* Generate a JSON object for METADATA. */
132 static json::object
*
133 json_from_metadata (const diagnostic_metadata
*metadata
)
135 json::object
*metadata_obj
= new json::object ();
137 if (metadata
->get_cwe ())
138 metadata_obj
->set ("cwe",
139 new json::integer_number (metadata
->get_cwe ()));
144 /* No-op implementation of "begin_diagnostic" for JSON output. */
147 json_begin_diagnostic (diagnostic_context
*, diagnostic_info
*)
151 /* Implementation of "end_diagnostic" for JSON output.
152 Generate a JSON object for DIAGNOSTIC, and store for output
153 within current diagnostic group. */
156 json_end_diagnostic (diagnostic_context
*context
, diagnostic_info
*diagnostic
,
157 diagnostic_t orig_diag_kind
)
159 json::object
*diag_obj
= new json::object ();
161 /* Get "kind" of diagnostic. */
163 static const char *const diagnostic_kind_text
[] = {
164 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
165 #include "diagnostic.def"
166 #undef DEFINE_DIAGNOSTIC_KIND
169 /* Lose the trailing ": ". */
170 const char *kind_text
= diagnostic_kind_text
[diagnostic
->kind
];
171 size_t len
= strlen (kind_text
);
172 gcc_assert (len
> 2);
173 gcc_assert (kind_text
[len
- 2] == ':');
174 gcc_assert (kind_text
[len
- 1] == ' ');
175 char *rstrip
= xstrdup (kind_text
);
176 rstrip
[len
- 2] = '\0';
177 diag_obj
->set ("kind", new json::string (rstrip
));
181 // FIXME: encoding of the message (json::string requires UTF-8)
182 diag_obj
->set ("message",
183 new json::string (pp_formatted_text (context
->printer
)));
184 pp_clear_output_area (context
->printer
);
187 option_text
= context
->option_name (context
, diagnostic
->option_index
,
188 orig_diag_kind
, diagnostic
->kind
);
191 diag_obj
->set ("option", new json::string (option_text
));
195 if (context
->get_option_url
)
197 char *option_url
= context
->get_option_url (context
,
198 diagnostic
->option_index
);
201 diag_obj
->set ("option_url", new json::string (option_url
));
206 /* If we've already emitted a diagnostic within this auto_diagnostic_group,
207 then add diag_obj to its "children" array. */
210 gcc_assert (cur_children_array
);
211 cur_children_array
->append (diag_obj
);
215 /* Otherwise, make diag_obj be the top-level object within the group;
216 add a "children" array and record the column origin. */
217 toplevel_array
->append (diag_obj
);
218 cur_group
= diag_obj
;
219 cur_children_array
= new json::array ();
220 diag_obj
->set ("children", cur_children_array
);
221 diag_obj
->set ("column-origin",
222 new json::integer_number (context
->column_origin
));
225 const rich_location
*richloc
= diagnostic
->richloc
;
227 json::array
*loc_array
= new json::array ();
228 diag_obj
->set ("locations", loc_array
);
230 for (unsigned int i
= 0; i
< richloc
->get_num_locations (); i
++)
232 const location_range
*loc_range
= richloc
->get_range (i
);
233 json::object
*loc_obj
= json_from_location_range (context
, loc_range
, i
);
235 loc_array
->append (loc_obj
);
238 if (richloc
->get_num_fixit_hints ())
240 json::array
*fixit_array
= new json::array ();
241 diag_obj
->set ("fixits", fixit_array
);
242 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
244 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
245 json::object
*fixit_obj
= json_from_fixit_hint (context
, hint
);
246 fixit_array
->append (fixit_obj
);
250 /* TODO: tree-ish things:
252 TODO: inlining information
253 TODO: macro expansion information. */
255 if (diagnostic
->metadata
)
257 json::object
*metadata_obj
= json_from_metadata (diagnostic
->metadata
);
258 diag_obj
->set ("metadata", metadata_obj
);
261 const diagnostic_path
*path
= richloc
->get_path ();
262 if (path
&& context
->make_json_for_path
)
264 json::value
*path_value
= context
->make_json_for_path (context
, path
);
265 diag_obj
->set ("path", path_value
);
269 /* No-op implementation of "begin_group_cb" for JSON output. */
272 json_begin_group (diagnostic_context
*)
276 /* Implementation of "end_group_cb" for JSON output. */
279 json_end_group (diagnostic_context
*)
282 cur_children_array
= NULL
;
285 /* Callback for final cleanup for JSON output. */
288 json_final_cb (diagnostic_context
*)
290 /* Flush the top-level array. */
291 toplevel_array
->dump (stderr
);
292 fprintf (stderr
, "\n");
293 delete toplevel_array
;
294 toplevel_array
= NULL
;
297 /* Set the output format for CONTEXT to FORMAT. */
300 diagnostic_output_format_init (diagnostic_context
*context
,
301 enum diagnostics_output_format format
)
307 case DIAGNOSTICS_OUTPUT_FORMAT_TEXT
:
308 /* The default; do nothing. */
311 case DIAGNOSTICS_OUTPUT_FORMAT_JSON
:
313 /* Set up top-level JSON array. */
314 if (toplevel_array
== NULL
)
315 toplevel_array
= new json::array ();
317 /* Override callbacks. */
318 context
->begin_diagnostic
= json_begin_diagnostic
;
319 context
->end_diagnostic
= json_end_diagnostic
;
320 context
->begin_group_cb
= json_begin_group
;
321 context
->end_group_cb
= json_end_group
;
322 context
->final_cb
= json_final_cb
;
323 context
->print_path
= NULL
; /* handled in json_end_diagnostic. */
325 /* The metadata is handled in JSON format, rather than as text. */
326 context
->show_cwe
= false;
328 /* The option is handled in JSON format, rather than as text. */
329 context
->show_option_requested
= false;
331 /* Don't colorize the text. */
332 pp_show_color (context
->printer
) = false;
342 /* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
343 but verify that we handle this gracefully. */
346 test_unknown_location ()
348 test_diagnostic_context dc
;
349 delete json_from_expanded_location (&dc
, UNKNOWN_LOCATION
);
352 /* Verify that we gracefully handle attempts to serialize bad
353 compound locations. */
356 test_bad_endpoints ()
358 location_t bad_endpoints
359 = make_location (BUILTINS_LOCATION
,
360 UNKNOWN_LOCATION
, UNKNOWN_LOCATION
);
362 location_range loc_range
;
363 loc_range
.m_loc
= bad_endpoints
;
364 loc_range
.m_range_display_kind
= SHOW_RANGE_WITH_CARET
;
365 loc_range
.m_label
= NULL
;
367 test_diagnostic_context dc
;
368 json::object
*obj
= json_from_location_range (&dc
, &loc_range
, 0);
369 /* We should have a "caret" value, but no "start" or "finish" values. */
370 ASSERT_TRUE (obj
!= NULL
);
371 ASSERT_TRUE (obj
->get ("caret") != NULL
);
372 ASSERT_TRUE (obj
->get ("start") == NULL
);
373 ASSERT_TRUE (obj
->get ("finish") == NULL
);
377 /* Run all of the selftests within this file. */
380 diagnostic_format_json_cc_tests ()
382 test_unknown_location ();
383 test_bad_endpoints ();
386 } // namespace selftest
388 #endif /* #if CHECKING_P */