Branch data Line data Source code
1 : : /* Support for -fdiagnostics-add-output= and -fdiagnostics-set-output=.
2 : : Copyright (C) 2024-2025 Free Software Foundation, Inc.
3 : :
4 : : This file is part of GCC.
5 : :
6 : : GCC is free software; you can redistribute it and/or modify it under
7 : : the terms of the GNU General Public License as published by the Free
8 : : Software Foundation; either version 3, or (at your option) any later
9 : : version.
10 : :
11 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : : for more details.
15 : :
16 : : You should have received a copy of the GNU General Public License
17 : : along with GCC; see the file COPYING3. If not see
18 : : <http://www.gnu.org/licenses/>. */
19 : :
20 : :
21 : : /* This file implements the options -fdiagnostics-add-output=,
22 : : -fdiagnostics-set-output=, and their domain-specific language. */
23 : :
24 : : #include "config.h"
25 : : #define INCLUDE_ARRAY
26 : : #define INCLUDE_STRING
27 : : #define INCLUDE_VECTOR
28 : : #include "system.h"
29 : : #include "coretypes.h"
30 : : #include "version.h"
31 : : #include "intl.h"
32 : : #include "diagnostic.h"
33 : : #include "diagnostic-color.h"
34 : : #include "diagnostic-format.h"
35 : : #include "diagnostic-format-html.h"
36 : : #include "diagnostic-format-text.h"
37 : : #include "diagnostic-format-sarif.h"
38 : : #include "selftest.h"
39 : : #include "selftest-diagnostic.h"
40 : : #include "pretty-print-markup.h"
41 : : #include "opts.h"
42 : : #include "options.h"
43 : :
44 : : /* A namespace for handling the DSL of the arguments of
45 : : -fdiagnostics-add-output= and -fdiagnostics-set-output=. */
46 : :
47 : : namespace gcc {
48 : : namespace diagnostics_output_spec {
49 : :
50 : : /* Decls. */
51 : :
52 : : struct context
53 : : {
54 : : public:
55 : 53 : context (const gcc_options &opts,
56 : : diagnostic_context &dc,
57 : : line_maps *location_mgr,
58 : : location_t loc,
59 : : const char *option_name)
60 : 53 : : m_opts (opts), m_dc (dc), m_location_mgr (location_mgr), m_loc (loc),
61 : 53 : m_option_name (option_name)
62 : : {}
63 : :
64 : : void
65 : : report_error (const char *gmsgid, ...) const
66 : : ATTRIBUTE_GCC_DIAG(2,3);
67 : :
68 : : void
69 : : report_unknown_key (const char *unparsed_arg,
70 : : const std::string &key,
71 : : const std::string &scheme_name,
72 : : auto_vec<const char *> &known_keys) const;
73 : :
74 : : void
75 : : report_missing_key (const char *unparsed_arg,
76 : : const std::string &key,
77 : : const std::string &scheme_name,
78 : : const char *metavar) const;
79 : :
80 : : diagnostic_output_file
81 : : open_output_file (label_text &&filename) const;
82 : :
83 : : const gcc_options &m_opts;
84 : : diagnostic_context &m_dc;
85 : : line_maps *m_location_mgr;
86 : : location_t m_loc;
87 : : const char *m_option_name;
88 : : };
89 : :
90 : 90 : struct scheme_name_and_params
91 : : {
92 : : std::string m_scheme_name;
93 : : std::vector<std::pair<std::string, std::string>> m_kvs;
94 : : };
95 : :
96 : : static std::unique_ptr<scheme_name_and_params>
97 : : parse (const context &ctxt, const char *unparsed_arg);
98 : :
99 : : /* Class for parsing the arguments of -fdiagnostics-add-output= and
100 : : -fdiagnostics-set-output=, and making diagnostic_output_format
101 : : instances (or issuing errors). */
102 : :
103 : 50 : class output_factory
104 : : {
105 : : public:
106 : : class scheme_handler
107 : : {
108 : : public:
109 : 75 : scheme_handler (std::string scheme_name)
110 : 75 : : m_scheme_name (std::move (scheme_name))
111 : : {}
112 : : virtual ~scheme_handler () {}
113 : :
114 : 45 : const std::string &get_scheme_name () const { return m_scheme_name; }
115 : :
116 : : virtual std::unique_ptr<diagnostic_output_format>
117 : : make_sink (const context &ctxt,
118 : : const char *unparsed_arg,
119 : : const scheme_name_and_params &parsed_arg) const = 0;
120 : :
121 : : protected:
122 : : bool
123 : 29 : parse_bool_value (const context &ctxt,
124 : : const char *unparsed_arg,
125 : : const std::string &key,
126 : : const std::string &value,
127 : : bool &out) const
128 : : {
129 : 29 : if (value == "yes")
130 : : {
131 : 13 : out = true;
132 : 13 : return true;
133 : : }
134 : 16 : else if (value == "no")
135 : : {
136 : 16 : out = false;
137 : 16 : return true;
138 : : }
139 : : else
140 : : {
141 : 0 : ctxt.report_error
142 : 0 : ("%<%s%s%>:"
143 : : " unexpected value %qs for key %qs; expected %qs or %qs",
144 : 0 : ctxt.m_option_name, unparsed_arg,
145 : : value.c_str (),
146 : : key.c_str (),
147 : : "yes", "no");
148 : :
149 : 0 : return false;
150 : : }
151 : : }
152 : : template <typename EnumType, size_t NumValues>
153 : : bool
154 : 3 : parse_enum_value (const context &ctxt,
155 : : const char *unparsed_arg,
156 : : const std::string &key,
157 : : const std::string &value,
158 : : const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
159 : : EnumType &out) const
160 : : {
161 : 5 : for (auto &iter : value_names)
162 : 5 : if (value == iter.first)
163 : : {
164 : 3 : out = iter.second;
165 : 3 : return true;
166 : : }
167 : :
168 : 0 : auto_vec<const char *> known_values;
169 : 0 : for (auto iter : value_names)
170 : 0 : known_values.safe_push (iter.first);
171 : 0 : pp_markup::comma_separated_quoted_strings e (known_values);
172 : : ctxt.report_error
173 : 0 : ("%<%s%s%>:"
174 : : " unexpected value %qs for key %qs; known values: %e",
175 : 0 : ctxt.m_option_name, unparsed_arg,
176 : : value.c_str (),
177 : : key.c_str (),
178 : : &e);
179 : 0 : return false;
180 : 0 : }
181 : :
182 : : private:
183 : : const std::string m_scheme_name;
184 : : };
185 : :
186 : : output_factory ();
187 : :
188 : : std::unique_ptr<diagnostic_output_format>
189 : : make_sink (const context &ctxt,
190 : : const char *unparsed_arg,
191 : : const scheme_name_and_params &parsed_arg);
192 : :
193 : : const scheme_handler *get_scheme_handler (const std::string &scheme_name);
194 : :
195 : : private:
196 : : std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
197 : : };
198 : :
199 : : class text_scheme_handler : public output_factory::scheme_handler
200 : : {
201 : : public:
202 : 25 : text_scheme_handler () : scheme_handler ("text") {}
203 : :
204 : : std::unique_ptr<diagnostic_output_format>
205 : : make_sink (const context &ctxt,
206 : : const char *unparsed_arg,
207 : : const scheme_name_and_params &parsed_arg) const final override;
208 : : };
209 : :
210 : : class sarif_scheme_handler : public output_factory::scheme_handler
211 : : {
212 : : public:
213 : 25 : sarif_scheme_handler () : scheme_handler ("sarif") {}
214 : :
215 : : std::unique_ptr<diagnostic_output_format>
216 : : make_sink (const context &ctxt,
217 : : const char *unparsed_arg,
218 : : const scheme_name_and_params &parsed_arg) const final override;
219 : : };
220 : :
221 : : class html_scheme_handler : public output_factory::scheme_handler
222 : : {
223 : : public:
224 : 25 : html_scheme_handler () : scheme_handler ("experimental-html") {}
225 : :
226 : : std::unique_ptr<diagnostic_output_format>
227 : : make_sink (const context &ctxt,
228 : : const char *unparsed_arg,
229 : : const scheme_name_and_params &parsed_arg) const final override;
230 : : };
231 : :
232 : : /* struct context. */
233 : :
234 : : void
235 : 16 : context::report_error (const char *gmsgid, ...) const
236 : : {
237 : 16 : m_dc.begin_group ();
238 : 16 : va_list ap;
239 : 16 : va_start (ap, gmsgid);
240 : 16 : rich_location richloc (m_location_mgr, m_loc);
241 : 16 : m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap, DK_ERROR);
242 : 16 : va_end (ap);
243 : 16 : m_dc.end_group ();
244 : 16 : }
245 : :
246 : : void
247 : 0 : context::report_unknown_key (const char *unparsed_arg,
248 : : const std::string &key,
249 : : const std::string &scheme_name,
250 : : auto_vec<const char *> &known_keys) const
251 : : {
252 : 0 : pp_markup::comma_separated_quoted_strings e (known_keys);
253 : 0 : report_error
254 : 0 : ("%<%s%s%>:"
255 : : " unknown key %qs for format %qs; known keys: %e",
256 : 0 : m_option_name, unparsed_arg,
257 : : key.c_str (), scheme_name.c_str (), &e);
258 : 0 : }
259 : :
260 : : void
261 : 0 : context::report_missing_key (const char *unparsed_arg,
262 : : const std::string &key,
263 : : const std::string &scheme_name,
264 : : const char *metavar) const
265 : : {
266 : 0 : report_error
267 : 0 : ("%<%s%s%>:"
268 : : " missing required key %qs for format %qs;"
269 : : " try %<%s%s:%s=%s%>",
270 : : m_option_name, unparsed_arg,
271 : : key.c_str (), scheme_name.c_str (),
272 : 0 : m_option_name, scheme_name.c_str (), key.c_str (), metavar);
273 : 0 : }
274 : :
275 : : std::unique_ptr<scheme_name_and_params>
276 : 53 : parse (const context &ctxt, const char *unparsed_arg)
277 : : {
278 : 53 : scheme_name_and_params result;
279 : 53 : if (const char *const colon = strchr (unparsed_arg, ':'))
280 : : {
281 : 46 : result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
282 : : /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
283 : 46 : const char *iter = colon + 1;
284 : 46 : const char *last_separator = ":";
285 : 96 : while (iter)
286 : : {
287 : : /* Look for a non-empty key string followed by '='. */
288 : 66 : const char *eq = strchr (iter, '=');
289 : 66 : if (eq == nullptr || eq == iter)
290 : : {
291 : : /* Missing '='. */
292 : 16 : ctxt.report_error
293 : 16 : ("%<%s%s%>:"
294 : : " expected KEY=VALUE-style parameter for format %qs"
295 : : " after %qs;"
296 : : " got %qs",
297 : 16 : ctxt.m_option_name, unparsed_arg,
298 : : result.m_scheme_name.c_str (),
299 : : last_separator,
300 : : iter);
301 : 16 : return nullptr;
302 : : }
303 : 50 : std::string key = std::string (iter, eq - iter);
304 : 50 : std::string value;
305 : 50 : const char *comma = strchr (iter, ',');
306 : 50 : if (comma)
307 : : {
308 : 20 : value = std::string (eq + 1, comma - (eq + 1));
309 : 20 : iter = comma + 1;
310 : 20 : last_separator = ",";
311 : : }
312 : : else
313 : : {
314 : 30 : value = std::string (eq + 1);
315 : 30 : iter = nullptr;
316 : : }
317 : 150 : result.m_kvs.push_back ({std::move (key), std::move (value)});
318 : 50 : }
319 : : }
320 : : else
321 : 7 : result.m_scheme_name = unparsed_arg;
322 : 37 : return std::make_unique<scheme_name_and_params> (std::move (result));
323 : 53 : }
324 : :
325 : : /* class output_factory::scheme_handler. */
326 : :
327 : : /* class output_factory. */
328 : :
329 : 25 : output_factory::output_factory ()
330 : : {
331 : 25 : m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> ());
332 : 25 : m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
333 : 25 : m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
334 : 25 : }
335 : :
336 : : const output_factory::scheme_handler *
337 : 25 : output_factory::get_scheme_handler (const std::string &scheme_name)
338 : : {
339 : 45 : for (auto &iter : m_scheme_handlers)
340 : 45 : if (iter->get_scheme_name () == scheme_name)
341 : 25 : return iter.get ();
342 : : return nullptr;
343 : : }
344 : :
345 : : std::unique_ptr<diagnostic_output_format>
346 : 25 : output_factory::make_sink (const context &ctxt,
347 : : const char *unparsed_arg,
348 : : const scheme_name_and_params &parsed_arg)
349 : : {
350 : 25 : auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
351 : 25 : if (!scheme_handler)
352 : : {
353 : 0 : auto_vec<const char *> strings;
354 : 0 : for (auto &iter : m_scheme_handlers)
355 : 0 : strings.safe_push (iter->get_scheme_name ().c_str ());
356 : 0 : pp_markup::comma_separated_quoted_strings e (strings);
357 : 0 : ctxt.report_error ("%<%s%s%>:"
358 : : " unrecognized format %qs; known formats: %e",
359 : 0 : ctxt.m_option_name, unparsed_arg,
360 : : parsed_arg.m_scheme_name.c_str (), &e);
361 : 0 : return nullptr;
362 : 0 : }
363 : :
364 : 25 : return scheme_handler->make_sink (ctxt, unparsed_arg, parsed_arg);
365 : : }
366 : :
367 : : /* class text_scheme_handler : public output_factory::scheme_handler. */
368 : :
369 : : std::unique_ptr<diagnostic_output_format>
370 : 12 : text_scheme_handler::make_sink (const context &ctxt,
371 : : const char *unparsed_arg,
372 : : const scheme_name_and_params &parsed_arg) const
373 : : {
374 : 12 : bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
375 : 12 : bool show_nesting = false;
376 : 12 : bool show_locations_in_nesting = true;
377 : 12 : bool show_levels = false;
378 : 34 : for (auto& iter : parsed_arg.m_kvs)
379 : : {
380 : 22 : const std::string &key = iter.first;
381 : 22 : const std::string &value = iter.second;
382 : 22 : if (key == "color")
383 : : {
384 : 0 : if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
385 : 0 : return nullptr;
386 : 22 : continue;
387 : : }
388 : 22 : if (key == "experimental-nesting")
389 : : {
390 : 12 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
391 : : show_nesting))
392 : 0 : return nullptr;
393 : 12 : continue;
394 : : }
395 : 10 : if (key == "experimental-nesting-show-locations")
396 : : {
397 : 9 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
398 : : show_locations_in_nesting))
399 : 0 : return nullptr;
400 : 9 : continue;
401 : : }
402 : 1 : if (key == "experimental-nesting-show-levels")
403 : : {
404 : 1 : if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
405 : 0 : return nullptr;
406 : 1 : continue;
407 : : }
408 : :
409 : : /* Key not found. */
410 : 0 : auto_vec<const char *> known_keys;
411 : 0 : known_keys.safe_push ("color");
412 : 0 : known_keys.safe_push ("experimental-nesting");
413 : 0 : known_keys.safe_push ("experimental-nesting-show-locations");
414 : 0 : known_keys.safe_push ("experimental-nesting-show-levels");
415 : 0 : ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
416 : : known_keys);
417 : 0 : return nullptr;
418 : 0 : }
419 : :
420 : 12 : auto sink = std::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
421 : 12 : sink->set_show_nesting (show_nesting);
422 : 12 : sink->set_show_locations_in_nesting (show_locations_in_nesting);
423 : 12 : sink->set_show_nesting_levels (show_levels);
424 : 12 : return sink;
425 : 12 : }
426 : :
427 : : diagnostic_output_file
428 : 2 : context::open_output_file (label_text &&filename) const
429 : : {
430 : 2 : FILE *outf = fopen (filename.get (), "w");
431 : 2 : if (!outf)
432 : : {
433 : 0 : rich_location richloc (m_location_mgr, m_loc);
434 : 0 : m_dc.emit_diagnostic_with_group
435 : 0 : (DK_ERROR, richloc, nullptr, 0,
436 : : "unable to open %qs: %m", filename.get ());
437 : 0 : return diagnostic_output_file (nullptr, false, std::move (filename));
438 : 0 : }
439 : 2 : return diagnostic_output_file (outf, true, std::move (filename));
440 : : }
441 : :
442 : : /* class sarif_scheme_handler : public output_factory::scheme_handler. */
443 : :
444 : : std::unique_ptr<diagnostic_output_format>
445 : 6 : sarif_scheme_handler::make_sink (const context &ctxt,
446 : : const char *unparsed_arg,
447 : : const scheme_name_and_params &parsed_arg) const
448 : : {
449 : 6 : label_text filename;
450 : 6 : enum sarif_serialization_kind serialization_kind
451 : : = sarif_serialization_kind::json;
452 : 6 : enum sarif_version version = sarif_version::v2_1_0;
453 : 11 : for (auto& iter : parsed_arg.m_kvs)
454 : : {
455 : 5 : const std::string &key = iter.first;
456 : 5 : const std::string &value = iter.second;
457 : 5 : if (key == "file")
458 : : {
459 : 2 : filename = label_text::take (xstrdup (value.c_str ()));
460 : 5 : continue;
461 : : }
462 : 3 : if (key == "serialization")
463 : : {
464 : 0 : static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
465 : : (size_t)sarif_serialization_kind::num_values> value_names
466 : : {{{"json", sarif_serialization_kind::json}}};
467 : :
468 : 0 : if (!parse_enum_value<enum sarif_serialization_kind>
469 : 0 : (ctxt, unparsed_arg,
470 : : key, value,
471 : : value_names,
472 : : serialization_kind))
473 : 0 : return nullptr;
474 : 0 : continue;
475 : 0 : }
476 : 3 : if (key == "version")
477 : : {
478 : 3 : static const std::array<std::pair<const char *, enum sarif_version>,
479 : : (size_t)sarif_version::num_versions> value_names
480 : : {{{"2.1", sarif_version::v2_1_0},
481 : : {"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
482 : :
483 : 3 : if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
484 : : key, value,
485 : : value_names,
486 : : version))
487 : 0 : return nullptr;
488 : 3 : continue;
489 : 3 : }
490 : :
491 : : /* Key not found. */
492 : 0 : auto_vec<const char *> known_keys;
493 : 0 : known_keys.safe_push ("file");
494 : 0 : known_keys.safe_push ("serialization");
495 : 0 : known_keys.safe_push ("version");
496 : 0 : ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
497 : : known_keys);
498 : 0 : return nullptr;
499 : 0 : }
500 : :
501 : 6 : diagnostic_output_file output_file;
502 : 6 : if (filename.get ())
503 : 2 : output_file = ctxt.open_output_file (std::move (filename));
504 : : else
505 : : // Default filename
506 : : {
507 : 4 : const char *basename = (ctxt.m_opts.x_dump_base_name
508 : : ? ctxt.m_opts.x_dump_base_name
509 : : : ctxt.m_opts.x_main_input_basename);
510 : 8 : output_file = diagnostic_output_format_open_sarif_file (ctxt.m_dc,
511 : : line_table,
512 : : basename,
513 : 4 : serialization_kind);
514 : : }
515 : 6 : if (!output_file)
516 : 0 : return nullptr;
517 : :
518 : 6 : sarif_generation_options sarif_gen_opts;
519 : 6 : sarif_gen_opts.m_version = version;
520 : :
521 : 6 : std::unique_ptr<sarif_serialization_format> serialization_obj;
522 : 6 : switch (serialization_kind)
523 : : {
524 : 0 : default:
525 : 0 : gcc_unreachable ();
526 : 6 : case sarif_serialization_kind::json:
527 : 6 : serialization_obj
528 : 6 : = std::make_unique<sarif_serialization_format_json> (true);
529 : 6 : break;
530 : : }
531 : :
532 : 6 : auto sink = make_sarif_sink (ctxt.m_dc,
533 : : *line_table,
534 : : std::move (serialization_obj),
535 : : sarif_gen_opts,
536 : 6 : std::move (output_file));
537 : 6 : return sink;
538 : 6 : }
539 : :
540 : : /* class html_scheme_handler : public output_factory::scheme_handler. */
541 : :
542 : : std::unique_ptr<diagnostic_output_format>
543 : 7 : html_scheme_handler::make_sink (const context &ctxt,
544 : : const char *unparsed_arg,
545 : : const scheme_name_and_params &parsed_arg) const
546 : : {
547 : 7 : bool css = true;
548 : 7 : label_text filename;
549 : 7 : bool javascript = true;
550 : 14 : for (auto& iter : parsed_arg.m_kvs)
551 : : {
552 : 7 : const std::string &key = iter.first;
553 : 7 : const std::string &value = iter.second;
554 : 7 : if (key == "css")
555 : : {
556 : 0 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
557 : : css))
558 : 0 : return nullptr;
559 : 7 : continue;
560 : : }
561 : 7 : if (key == "file")
562 : : {
563 : 0 : filename = label_text::take (xstrdup (value.c_str ()));
564 : 0 : continue;
565 : : }
566 : 7 : if (key == "javascript")
567 : : {
568 : 7 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
569 : : javascript))
570 : 0 : return nullptr;
571 : 7 : continue;
572 : : }
573 : :
574 : : /* Key not found. */
575 : 0 : auto_vec<const char *> known_keys;
576 : 0 : known_keys.safe_push ("css");
577 : 0 : known_keys.safe_push ("file");
578 : 0 : known_keys.safe_push ("javascript");
579 : 0 : ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
580 : : known_keys);
581 : 0 : return nullptr;
582 : 0 : }
583 : :
584 : 7 : diagnostic_output_file output_file;
585 : 7 : if (filename.get ())
586 : 0 : output_file = ctxt.open_output_file (std::move (filename));
587 : : else
588 : : // Default filename
589 : : {
590 : 7 : const char *basename = (ctxt.m_opts.x_dump_base_name
591 : : ? ctxt.m_opts.x_dump_base_name
592 : : : ctxt.m_opts.x_main_input_basename);
593 : 14 : output_file = diagnostic_output_format_open_html_file (ctxt.m_dc,
594 : : line_table,
595 : 7 : basename);
596 : : }
597 : 7 : if (!output_file)
598 : 0 : return nullptr;
599 : :
600 : 7 : html_generation_options html_gen_opts;
601 : 7 : html_gen_opts.m_css = css;
602 : 7 : html_gen_opts.m_javascript = javascript;
603 : :
604 : 14 : auto sink = make_html_sink (ctxt.m_dc,
605 : : *line_table,
606 : : html_gen_opts,
607 : 7 : std::move (output_file));
608 : 7 : return sink;
609 : 7 : }
610 : :
611 : : } // namespace diagnostics_output_spec
612 : : } // namespace gcc
613 : :
614 : : void
615 : 11 : handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
616 : : diagnostic_context &dc,
617 : : const char *arg,
618 : : location_t loc)
619 : : {
620 : 11 : gcc_assert (arg);
621 : 11 : gcc_assert (line_table);
622 : :
623 : 11 : const char *const option_name = "-fdiagnostics-add-output=";
624 : 11 : gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
625 : 11 : option_name);
626 : 11 : auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
627 : 11 : if (!result)
628 : : return;
629 : :
630 : 11 : gcc::diagnostics_output_spec::output_factory factory;
631 : 11 : auto sink = factory.make_sink (ctxt, arg, *result);
632 : 11 : if (!sink)
633 : 0 : return;
634 : :
635 : 11 : sink->set_main_input_filename (opts.x_main_input_filename);
636 : 11 : dc.add_sink (std::move (sink));
637 : 11 : }
638 : :
639 : : void
640 : 14 : handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
641 : : diagnostic_context &dc,
642 : : const char *arg,
643 : : location_t loc)
644 : : {
645 : 14 : gcc_assert (arg);
646 : 14 : gcc_assert (line_table);
647 : :
648 : 14 : const char *const option_name = "-fdiagnostics-set-output=";
649 : 14 : gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
650 : 14 : option_name);
651 : 14 : auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
652 : 14 : if (!result)
653 : : return;
654 : :
655 : 14 : gcc::diagnostics_output_spec::output_factory factory;
656 : 14 : auto sink = factory.make_sink (ctxt, arg, *result);
657 : 14 : if (!sink)
658 : 0 : return;
659 : :
660 : 14 : sink->set_main_input_filename (opts.x_main_input_filename);
661 : 14 : dc.set_output_format (std::move (sink));
662 : 14 : }
663 : :
664 : : #if CHECKING_P
665 : :
666 : : namespace selftest {
667 : :
668 : : /* RAII class to temporarily override "progname" to the
669 : : string "PROGNAME". */
670 : :
671 : : class auto_fix_progname
672 : : {
673 : : public:
674 : 4 : auto_fix_progname ()
675 : 4 : {
676 : 4 : m_old_progname = progname;
677 : 4 : progname = "PROGNAME";
678 : : }
679 : :
680 : 4 : ~auto_fix_progname ()
681 : : {
682 : 4 : progname = m_old_progname;
683 : 4 : }
684 : :
685 : : private:
686 : : const char *m_old_progname;
687 : : };
688 : :
689 : 56 : struct parser_test
690 : : {
691 : 28 : parser_test ()
692 : 28 : : m_opts (),
693 : 28 : m_dc (),
694 : 28 : m_ctxt (m_opts, m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
695 : 28 : m_fmt (m_dc.get_output_format (0))
696 : : {
697 : 28 : pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
698 : 28 : }
699 : :
700 : : std::unique_ptr<gcc::diagnostics_output_spec::scheme_name_and_params>
701 : 28 : parse (const char *unparsed_arg)
702 : : {
703 : 28 : return gcc::diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
704 : : }
705 : :
706 : 28 : bool execution_failed_p () const
707 : : {
708 : 28 : return m_dc.execution_failed_p ();
709 : : }
710 : :
711 : : const char *
712 : 16 : get_diagnostic_text () const
713 : : {
714 : 16 : return pp_formatted_text (m_fmt.get_printer ());
715 : : }
716 : :
717 : : private:
718 : : const gcc_options m_opts;
719 : : test_diagnostic_context m_dc;
720 : : gcc::diagnostics_output_spec::context m_ctxt;
721 : : diagnostic_output_format &m_fmt;
722 : : };
723 : :
724 : : /* Selftests. */
725 : :
726 : : static void
727 : 4 : test_output_arg_parsing ()
728 : : {
729 : 4 : auto_fix_quotes fix_quotes;
730 : 4 : auto_fix_progname fix_progname;
731 : :
732 : : /* Minimal correct example. */
733 : 4 : {
734 : 4 : parser_test pt;
735 : 4 : auto result = pt.parse ("foo");
736 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
737 : 4 : ASSERT_EQ (result->m_kvs.size (), 0);
738 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
739 : 4 : }
740 : :
741 : : /* Stray trailing colon with no key/value pairs. */
742 : 4 : {
743 : 4 : parser_test pt;
744 : 4 : auto result = pt.parse ("foo:");
745 : 4 : ASSERT_EQ (result, nullptr);
746 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
747 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
748 : : "PROGNAME: error: `-fOPTION=foo:':"
749 : : " expected KEY=VALUE-style parameter for format `foo'"
750 : : " after `:';"
751 : : " got `'\n");
752 : 4 : }
753 : :
754 : : /* No key before '='. */
755 : 4 : {
756 : 4 : parser_test pt;
757 : 4 : auto result = pt.parse ("foo:=");
758 : 4 : ASSERT_EQ (result, nullptr);
759 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
760 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
761 : : "PROGNAME: error: `-fOPTION=foo:=':"
762 : : " expected KEY=VALUE-style parameter for format `foo'"
763 : : " after `:';"
764 : : " got `='\n");
765 : 4 : }
766 : :
767 : : /* No value for key. */
768 : 4 : {
769 : 4 : parser_test pt;
770 : 4 : auto result = pt.parse ("foo:key,");
771 : 4 : ASSERT_EQ (result, nullptr);
772 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
773 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
774 : : "PROGNAME: error: `-fOPTION=foo:key,':"
775 : : " expected KEY=VALUE-style parameter for format `foo'"
776 : : " after `:';"
777 : : " got `key,'\n");
778 : 4 : }
779 : :
780 : : /* Correct example, with one key/value pair. */
781 : 4 : {
782 : 4 : parser_test pt;
783 : 4 : auto result = pt.parse ("foo:key=value");
784 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
785 : 4 : ASSERT_EQ (result->m_kvs.size (), 1);
786 : 4 : ASSERT_EQ (result->m_kvs[0].first, "key");
787 : 4 : ASSERT_EQ (result->m_kvs[0].second, "value");
788 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
789 : 4 : }
790 : :
791 : : /* Stray trailing comma. */
792 : 4 : {
793 : 4 : parser_test pt;
794 : 4 : auto result = pt.parse ("foo:key=value,");
795 : 4 : ASSERT_EQ (result, nullptr);
796 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
797 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
798 : : "PROGNAME: error: `-fOPTION=foo:key=value,':"
799 : : " expected KEY=VALUE-style parameter for format `foo'"
800 : : " after `,';"
801 : : " got `'\n");
802 : 4 : }
803 : :
804 : : /* Correct example, with two key/value pairs. */
805 : 4 : {
806 : 4 : parser_test pt;
807 : 4 : auto result = pt.parse ("foo:color=red,shape=circle");
808 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
809 : 4 : ASSERT_EQ (result->m_kvs.size (), 2);
810 : 4 : ASSERT_EQ (result->m_kvs[0].first, "color");
811 : 4 : ASSERT_EQ (result->m_kvs[0].second, "red");
812 : 4 : ASSERT_EQ (result->m_kvs[1].first, "shape");
813 : 4 : ASSERT_EQ (result->m_kvs[1].second, "circle");
814 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
815 : 4 : }
816 : 4 : }
817 : :
818 : : /* Run all of the selftests within this file. */
819 : :
820 : : void
821 : 4 : opts_diagnostic_cc_tests ()
822 : : {
823 : 4 : test_output_arg_parsing ();
824 : 4 : }
825 : :
826 : : } // namespace selftest
827 : :
828 : : #endif /* #if CHECKING_P */
|