1 /* JSON output for diagnostics
2    Copyright (C) 2018-2019 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 
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "diagnostic.h"
26 #include "json.h"
27 
28 /* The top-level JSON array of pending diagnostics.  */
29 
30 static json::array *toplevel_array;
31 
32 /* The JSON object for the current diagnostic group.  */
33 
34 static json::object *cur_group;
35 
36 /* The JSON array for the "children" array within the current diagnostic
37    group.  */
38 
39 static json::array *cur_children_array;
40 
41 /* Generate a JSON object for LOC.  */
42 
43 static json::object *
json_from_expanded_location(location_t loc)44 json_from_expanded_location (location_t loc)
45 {
46   expanded_location exploc = expand_location (loc);
47   json::object *result = new json::object ();
48   result->set ("file", new json::string (exploc.file));
49   result->set ("line", new json::number (exploc.line));
50   result->set ("column", new json::number (exploc.column));
51   return result;
52 }
53 
54 /* Generate a JSON object for LOC_RANGE.  */
55 
56 static json::object *
json_from_location_range(const location_range * loc_range,unsigned range_idx)57 json_from_location_range (const location_range *loc_range, unsigned range_idx)
58 {
59   location_t caret_loc = get_pure_location (loc_range->m_loc);
60 
61   if (caret_loc == UNKNOWN_LOCATION)
62     return NULL;
63 
64   location_t start_loc = get_start (loc_range->m_loc);
65   location_t finish_loc = get_finish (loc_range->m_loc);
66 
67   json::object *result = new json::object ();
68   result->set ("caret", json_from_expanded_location (caret_loc));
69   if (start_loc != caret_loc)
70     result->set ("start", json_from_expanded_location (start_loc));
71   if (finish_loc != caret_loc)
72     result->set ("finish", json_from_expanded_location (finish_loc));
73 
74   if (loc_range->m_label)
75     {
76       label_text text;
77       text = loc_range->m_label->get_text (range_idx);
78       if (text.m_buffer)
79 	result->set ("label", new json::string (text.m_buffer));
80       text.maybe_free ();
81     }
82 
83   return result;
84 }
85 
86 /* Generate a JSON object for HINT.  */
87 
88 static json::object *
json_from_fixit_hint(const fixit_hint * hint)89 json_from_fixit_hint (const fixit_hint *hint)
90 {
91   json::object *fixit_obj = new json::object ();
92 
93   location_t start_loc = hint->get_start_loc ();
94   fixit_obj->set ("start", json_from_expanded_location (start_loc));
95   location_t next_loc = hint->get_next_loc ();
96   fixit_obj->set ("next", json_from_expanded_location (next_loc));
97   fixit_obj->set ("string", new json::string (hint->get_string ()));
98 
99   return fixit_obj;
100 }
101 
102 /* No-op implementation of "begin_diagnostic" for JSON output.  */
103 
104 static void
json_begin_diagnostic(diagnostic_context *,diagnostic_info *)105 json_begin_diagnostic (diagnostic_context *, diagnostic_info *)
106 {
107 }
108 
109 /* Implementation of "end_diagnostic" for JSON output.
110    Generate a JSON object for DIAGNOSTIC, and store for output
111    within current diagnostic group.  */
112 
113 static void
json_end_diagnostic(diagnostic_context * context,diagnostic_info * diagnostic,diagnostic_t orig_diag_kind)114 json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
115 		     diagnostic_t orig_diag_kind)
116 {
117   json::object *diag_obj = new json::object ();
118 
119   /* Get "kind" of diagnostic.  */
120   {
121     static const char *const diagnostic_kind_text[] = {
122 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
123 #include "diagnostic.def"
124 #undef DEFINE_DIAGNOSTIC_KIND
125       "must-not-happen"
126     };
127     /* Lose the trailing ": ".  */
128     const char *kind_text = diagnostic_kind_text[diagnostic->kind];
129     size_t len = strlen (kind_text);
130     gcc_assert (len > 2);
131     gcc_assert (kind_text[len - 2] == ':');
132     gcc_assert (kind_text[len - 1] == ' ');
133     char *rstrip = xstrdup (kind_text);
134     rstrip[len - 2] = '\0';
135     diag_obj->set ("kind", new json::string (rstrip));
136     free (rstrip);
137   }
138 
139   // FIXME: encoding of the message (json::string requires UTF-8)
140   diag_obj->set ("message",
141 		 new json::string (pp_formatted_text (context->printer)));
142   pp_clear_output_area (context->printer);
143 
144   char *option_text;
145   option_text = context->option_name (context, diagnostic->option_index,
146 				      orig_diag_kind, diagnostic->kind);
147   if (option_text)
148     {
149       diag_obj->set ("option", new json::string (option_text));
150       free (option_text);
151     }
152 
153   /* If we've already emitted a diagnostic within this auto_diagnostic_group,
154      then add diag_obj to its "children" array.  */
155   if (cur_group)
156     {
157       gcc_assert (cur_children_array);
158       cur_children_array->append (diag_obj);
159     }
160   else
161     {
162       /* Otherwise, make diag_obj be the top-level object within the group;
163 	 add a "children" array.  */
164       toplevel_array->append (diag_obj);
165       cur_group = diag_obj;
166       cur_children_array = new json::array ();
167       diag_obj->set ("children", cur_children_array);
168     }
169 
170   const rich_location *richloc = diagnostic->richloc;
171 
172   json::array *loc_array = new json::array ();
173   diag_obj->set ("locations", loc_array);
174 
175   for (unsigned int i = 0; i < richloc->get_num_locations (); i++)
176     {
177       const location_range *loc_range = richloc->get_range (i);
178       json::object *loc_obj = json_from_location_range (loc_range, i);
179       if (loc_obj)
180 	loc_array->append (loc_obj);
181     }
182 
183   if (richloc->get_num_fixit_hints ())
184     {
185       json::array *fixit_array = new json::array ();
186       diag_obj->set ("fixits", fixit_array);
187       for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
188 	{
189 	  const fixit_hint *hint = richloc->get_fixit_hint (i);
190 	  json::object *fixit_obj = json_from_fixit_hint (hint);
191 	  fixit_array->append (fixit_obj);
192 	}
193     }
194 
195   /* TODO: tree-ish things:
196      TODO: functions
197      TODO: inlining information
198      TODO: macro expansion information.  */
199 }
200 
201 /* No-op implementation of "begin_group_cb" for JSON output.  */
202 
203 static void
json_begin_group(diagnostic_context *)204 json_begin_group (diagnostic_context *)
205 {
206 }
207 
208 /* Implementation of "end_group_cb" for JSON output.  */
209 
210 static void
json_end_group(diagnostic_context *)211 json_end_group (diagnostic_context *)
212 {
213   cur_group = NULL;
214   cur_children_array = NULL;
215 }
216 
217 /* Callback for final cleanup for JSON output.  */
218 
219 static void
json_final_cb(diagnostic_context *)220 json_final_cb (diagnostic_context *)
221 {
222   /* Flush the top-level array.  */
223   toplevel_array->dump (stderr);
224   fprintf (stderr, "\n");
225   delete toplevel_array;
226   toplevel_array = NULL;
227 }
228 
229 /* Set the output format for CONTEXT to FORMAT.  */
230 
231 void
diagnostic_output_format_init(diagnostic_context * context,enum diagnostics_output_format format)232 diagnostic_output_format_init (diagnostic_context *context,
233 			       enum diagnostics_output_format format)
234 {
235   switch (format)
236     {
237     default:
238       gcc_unreachable ();
239     case DIAGNOSTICS_OUTPUT_FORMAT_TEXT:
240       /* The default; do nothing.  */
241       break;
242 
243     case DIAGNOSTICS_OUTPUT_FORMAT_JSON:
244       {
245 	/* Set up top-level JSON array.  */
246 	if (toplevel_array == NULL)
247 	  toplevel_array = new json::array ();
248 
249 	/* Override callbacks.  */
250 	context->begin_diagnostic = json_begin_diagnostic;
251 	context->end_diagnostic = json_end_diagnostic;
252 	context->begin_group_cb = json_begin_group;
253 	context->end_group_cb =  json_end_group;
254 	context->final_cb =  json_final_cb;
255 
256 	/* The option is handled in JSON format, rather than as text.  */
257 	context->show_option_requested = false;
258 
259 	/* Don't colorize the text.  */
260 	pp_show_color (context->printer) = false;
261       }
262       break;
263     }
264 }
265