Branch data Line data Source code
1 : : /* Support for -fdiagnostics-add-output= and -fdiagnostics-set-output=.
2 : : Copyright (C) 2024 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-text.h"
36 : : #include "diagnostic-format-sarif.h"
37 : : #include "selftest.h"
38 : : #include "selftest-diagnostic.h"
39 : : #include "pretty-print-markup.h"
40 : : #include "opts.h"
41 : : #include "options.h"
42 : : #include "make-unique.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 : 46 : context (const gcc_options &opts,
56 : : diagnostic_context &dc,
57 : : line_maps *location_mgr,
58 : : location_t loc,
59 : : const char *option_name)
60 : 46 : : m_opts (opts), m_dc (dc), m_location_mgr (location_mgr), m_loc (loc),
61 : 46 : 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 : 76 : 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 : 36 : class output_factory
104 : : {
105 : : public:
106 : : class scheme_handler
107 : : {
108 : : public:
109 : 36 : scheme_handler (std::string scheme_name)
110 : 36 : : m_scheme_name (std::move (scheme_name))
111 : : {}
112 : : virtual ~scheme_handler () {}
113 : :
114 : 24 : 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 : 22 : 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 : 22 : if (value == "yes")
130 : : {
131 : 13 : out = true;
132 : 13 : return true;
133 : : }
134 : 9 : else if (value == "no")
135 : : {
136 : 9 : out = false;
137 : 9 : 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 : 18 : 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 : 18 : 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 : : /* struct context. */
222 : :
223 : : void
224 : 16 : context::report_error (const char *gmsgid, ...) const
225 : : {
226 : 16 : m_dc.begin_group ();
227 : 16 : va_list ap;
228 : 16 : va_start (ap, gmsgid);
229 : 16 : rich_location richloc (m_location_mgr, m_loc);
230 : 16 : m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap, DK_ERROR);
231 : 16 : va_end (ap);
232 : 16 : m_dc.end_group ();
233 : 16 : }
234 : :
235 : : void
236 : 0 : context::report_unknown_key (const char *unparsed_arg,
237 : : const std::string &key,
238 : : const std::string &scheme_name,
239 : : auto_vec<const char *> &known_keys) const
240 : : {
241 : 0 : pp_markup::comma_separated_quoted_strings e (known_keys);
242 : 0 : report_error
243 : 0 : ("%<%s%s%>:"
244 : : " unknown key %qs for format %qs; known keys: %e",
245 : 0 : m_option_name, unparsed_arg,
246 : : key.c_str (), scheme_name.c_str (), &e);
247 : 0 : }
248 : :
249 : : void
250 : 0 : context::report_missing_key (const char *unparsed_arg,
251 : : const std::string &key,
252 : : const std::string &scheme_name,
253 : : const char *metavar) const
254 : : {
255 : 0 : report_error
256 : 0 : ("%<%s%s%>:"
257 : : " missing required key %qs for format %qs;"
258 : : " try %<%s%s:%s=%s%>",
259 : : m_option_name, unparsed_arg,
260 : : key.c_str (), scheme_name.c_str (),
261 : 0 : m_option_name, scheme_name.c_str (), key.c_str (), metavar);
262 : 0 : }
263 : :
264 : : std::unique_ptr<scheme_name_and_params>
265 : 46 : parse (const context &ctxt, const char *unparsed_arg)
266 : : {
267 : 46 : scheme_name_and_params result;
268 : 46 : if (const char *const colon = strchr (unparsed_arg, ':'))
269 : : {
270 : 39 : result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
271 : : /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
272 : 39 : const char *iter = colon + 1;
273 : 39 : const char *last_separator = ":";
274 : 82 : while (iter)
275 : : {
276 : : /* Look for a non-empty key string followed by '='. */
277 : 59 : const char *eq = strchr (iter, '=');
278 : 59 : if (eq == nullptr || eq == iter)
279 : : {
280 : : /* Missing '='. */
281 : 16 : ctxt.report_error
282 : 16 : ("%<%s%s%>:"
283 : : " expected KEY=VALUE-style parameter for format %qs"
284 : : " after %qs;"
285 : : " got %qs",
286 : 16 : ctxt.m_option_name, unparsed_arg,
287 : : result.m_scheme_name.c_str (),
288 : : last_separator,
289 : : iter);
290 : 16 : return nullptr;
291 : : }
292 : 43 : std::string key = std::string (iter, eq - iter);
293 : 43 : std::string value;
294 : 43 : const char *comma = strchr (iter, ',');
295 : 43 : if (comma)
296 : : {
297 : 20 : value = std::string (eq + 1, comma - (eq + 1));
298 : 20 : iter = comma + 1;
299 : 20 : last_separator = ",";
300 : : }
301 : : else
302 : : {
303 : 23 : value = std::string (eq + 1);
304 : 23 : iter = nullptr;
305 : : }
306 : 129 : result.m_kvs.push_back ({std::move (key), std::move (value)});
307 : 43 : }
308 : : }
309 : : else
310 : 7 : result.m_scheme_name = unparsed_arg;
311 : 30 : return ::make_unique<scheme_name_and_params> (std::move (result));
312 : 46 : }
313 : :
314 : : /* class output_factory::scheme_handler. */
315 : :
316 : : /* class output_factory. */
317 : :
318 : 18 : output_factory::output_factory ()
319 : : {
320 : 18 : m_scheme_handlers.push_back (::make_unique<text_scheme_handler> ());
321 : 18 : m_scheme_handlers.push_back (::make_unique<sarif_scheme_handler> ());
322 : 18 : }
323 : :
324 : : const output_factory::scheme_handler *
325 : 18 : output_factory::get_scheme_handler (const std::string &scheme_name)
326 : : {
327 : 24 : for (auto &iter : m_scheme_handlers)
328 : 24 : if (iter->get_scheme_name () == scheme_name)
329 : 18 : return iter.get ();
330 : : return nullptr;
331 : : }
332 : :
333 : : std::unique_ptr<diagnostic_output_format>
334 : 18 : output_factory::make_sink (const context &ctxt,
335 : : const char *unparsed_arg,
336 : : const scheme_name_and_params &parsed_arg)
337 : : {
338 : 18 : auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
339 : 18 : if (!scheme_handler)
340 : : {
341 : 0 : auto_vec<const char *> strings;
342 : 0 : for (auto &iter : m_scheme_handlers)
343 : 0 : strings.safe_push (iter->get_scheme_name ().c_str ());
344 : 0 : pp_markup::comma_separated_quoted_strings e (strings);
345 : 0 : ctxt.report_error ("%<%s%s%>:"
346 : : " unrecognized format %qs; known formats: %e",
347 : 0 : ctxt.m_option_name, unparsed_arg,
348 : : parsed_arg.m_scheme_name.c_str (), &e);
349 : 0 : return nullptr;
350 : 0 : }
351 : :
352 : 18 : return scheme_handler->make_sink (ctxt, unparsed_arg, parsed_arg);
353 : : }
354 : :
355 : : /* class text_scheme_handler : public output_factory::scheme_handler. */
356 : :
357 : : std::unique_ptr<diagnostic_output_format>
358 : 12 : text_scheme_handler::make_sink (const context &ctxt,
359 : : const char *unparsed_arg,
360 : : const scheme_name_and_params &parsed_arg) const
361 : : {
362 : 12 : bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
363 : 12 : bool show_nesting = false;
364 : 12 : bool show_locations_in_nesting = true;
365 : 12 : bool show_levels = false;
366 : 34 : for (auto& iter : parsed_arg.m_kvs)
367 : : {
368 : 22 : const std::string &key = iter.first;
369 : 22 : const std::string &value = iter.second;
370 : 22 : if (key == "color")
371 : : {
372 : 0 : if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
373 : 0 : return nullptr;
374 : 22 : continue;
375 : : }
376 : 22 : if (key == "experimental-nesting")
377 : : {
378 : 12 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
379 : : show_nesting))
380 : 0 : return nullptr;
381 : 12 : continue;
382 : : }
383 : 10 : if (key == "experimental-nesting-show-locations")
384 : : {
385 : 9 : if (!parse_bool_value (ctxt, unparsed_arg, key, value,
386 : : show_locations_in_nesting))
387 : 0 : return nullptr;
388 : 9 : continue;
389 : : }
390 : 1 : if (key == "experimental-nesting-show-levels")
391 : : {
392 : 1 : if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
393 : 0 : return nullptr;
394 : 1 : continue;
395 : : }
396 : :
397 : : /* Key not found. */
398 : 0 : auto_vec<const char *> known_keys;
399 : 0 : known_keys.safe_push ("color");
400 : 0 : known_keys.safe_push ("experimental-nesting");
401 : 0 : known_keys.safe_push ("experimental-nesting-show-locations");
402 : 0 : known_keys.safe_push ("experimental-nesting-show-levels");
403 : 0 : ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
404 : : known_keys);
405 : 0 : return nullptr;
406 : 0 : }
407 : :
408 : 12 : auto sink = ::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
409 : 12 : sink->set_show_nesting (show_nesting);
410 : 12 : sink->set_show_locations_in_nesting (show_locations_in_nesting);
411 : 12 : sink->set_show_nesting_levels (show_levels);
412 : 12 : return sink;
413 : 12 : }
414 : :
415 : : diagnostic_output_file
416 : 2 : context::open_output_file (label_text &&filename) const
417 : : {
418 : 2 : FILE *outf = fopen (filename.get (), "w");
419 : 2 : if (!outf)
420 : : {
421 : 0 : rich_location richloc (m_location_mgr, m_loc);
422 : 0 : m_dc.emit_diagnostic_with_group
423 : 0 : (DK_ERROR, richloc, nullptr, 0,
424 : : "unable to open %qs: %m", filename.get ());
425 : 0 : return diagnostic_output_file (nullptr, false, std::move (filename));
426 : 0 : }
427 : 2 : return diagnostic_output_file (outf, true, std::move (filename));
428 : : }
429 : :
430 : : /* class sarif_scheme_handler : public output_factory::scheme_handler. */
431 : :
432 : : std::unique_ptr<diagnostic_output_format>
433 : 6 : sarif_scheme_handler::make_sink (const context &ctxt,
434 : : const char *unparsed_arg,
435 : : const scheme_name_and_params &parsed_arg) const
436 : : {
437 : 6 : enum sarif_version version = sarif_version::v2_1_0;
438 : 6 : label_text filename;
439 : 11 : for (auto& iter : parsed_arg.m_kvs)
440 : : {
441 : 5 : const std::string &key = iter.first;
442 : 5 : const std::string &value = iter.second;
443 : 5 : if (key == "version")
444 : : {
445 : 3 : static const std::array<std::pair<const char *, enum sarif_version>,
446 : : (size_t)sarif_version::num_versions> value_names
447 : : {{{"2.1", sarif_version::v2_1_0},
448 : : {"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
449 : :
450 : 3 : if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
451 : : key, value,
452 : : value_names,
453 : : version))
454 : 0 : return nullptr;
455 : 5 : continue;
456 : 3 : }
457 : 2 : if (key == "file")
458 : : {
459 : 2 : filename = label_text::take (xstrdup (value.c_str ()));
460 : 2 : continue;
461 : : }
462 : :
463 : : /* Key not found. */
464 : 0 : auto_vec<const char *> known_keys;
465 : 0 : known_keys.safe_push ("file");
466 : 0 : known_keys.safe_push ("version");
467 : 0 : ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
468 : : known_keys);
469 : 0 : return nullptr;
470 : 0 : }
471 : :
472 : 6 : diagnostic_output_file output_file;
473 : 6 : if (filename.get ())
474 : 2 : output_file = ctxt.open_output_file (std::move (filename));
475 : : else
476 : : // Default filename
477 : : {
478 : 4 : const char *basename = (ctxt.m_opts.x_dump_base_name
479 : : ? ctxt.m_opts.x_dump_base_name
480 : : : ctxt.m_opts.x_main_input_basename);
481 : 8 : output_file = diagnostic_output_format_open_sarif_file (ctxt.m_dc,
482 : : line_table,
483 : 4 : basename);
484 : : }
485 : 6 : if (!output_file)
486 : 0 : return nullptr;
487 : :
488 : 12 : auto sink = make_sarif_sink (ctxt.m_dc,
489 : : *line_table,
490 : 6 : ctxt.m_opts.x_main_input_filename,
491 : : version,
492 : 6 : std::move (output_file));
493 : 6 : return sink;
494 : 6 : }
495 : :
496 : : } // namespace diagnostics_output_spec
497 : : } // namespace gcc
498 : :
499 : : void
500 : 5 : handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
501 : : diagnostic_context &dc,
502 : : const char *arg,
503 : : location_t loc)
504 : : {
505 : 5 : gcc_assert (arg);
506 : 5 : gcc_assert (line_table);
507 : :
508 : 5 : const char *const option_name = "-fdiagnostics-add-output=";
509 : 5 : gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
510 : 5 : option_name);
511 : 5 : auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
512 : 5 : if (!result)
513 : : return;
514 : :
515 : 5 : gcc::diagnostics_output_spec::output_factory factory;
516 : 5 : auto sink = factory.make_sink (ctxt, arg, *result);
517 : 5 : if (!sink)
518 : 0 : return;
519 : :
520 : 5 : dc.add_sink (std::move (sink));
521 : 5 : }
522 : :
523 : : void
524 : 13 : handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
525 : : diagnostic_context &dc,
526 : : const char *arg,
527 : : location_t loc)
528 : : {
529 : 13 : gcc_assert (arg);
530 : 13 : gcc_assert (line_table);
531 : :
532 : 13 : const char *const option_name = "-fdiagnostics-set-output=";
533 : 13 : gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
534 : 13 : option_name);
535 : 13 : auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
536 : 13 : if (!result)
537 : : return;
538 : :
539 : 13 : gcc::diagnostics_output_spec::output_factory factory;
540 : 13 : auto sink = factory.make_sink (ctxt, arg, *result);
541 : 13 : if (!sink)
542 : 0 : return;
543 : :
544 : 13 : dc.set_output_format (std::move (sink));
545 : 13 : }
546 : :
547 : : #if CHECKING_P
548 : :
549 : : namespace selftest {
550 : :
551 : : /* RAII class to temporarily override "progname" to the
552 : : string "PROGNAME". */
553 : :
554 : : class auto_fix_progname
555 : : {
556 : : public:
557 : 4 : auto_fix_progname ()
558 : 4 : {
559 : 4 : m_old_progname = progname;
560 : 4 : progname = "PROGNAME";
561 : : }
562 : :
563 : 4 : ~auto_fix_progname ()
564 : : {
565 : 4 : progname = m_old_progname;
566 : 4 : }
567 : :
568 : : private:
569 : : const char *m_old_progname;
570 : : };
571 : :
572 : 56 : struct parser_test
573 : : {
574 : 28 : parser_test ()
575 : 28 : : m_opts (),
576 : 28 : m_dc (),
577 : 28 : m_ctxt (m_opts, m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
578 : 28 : m_fmt (m_dc.get_output_format (0))
579 : : {
580 : 28 : pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
581 : 28 : }
582 : :
583 : : std::unique_ptr<gcc::diagnostics_output_spec::scheme_name_and_params>
584 : 28 : parse (const char *unparsed_arg)
585 : : {
586 : 28 : return gcc::diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
587 : : }
588 : :
589 : 28 : bool execution_failed_p () const
590 : : {
591 : 28 : return m_dc.execution_failed_p ();
592 : : }
593 : :
594 : : const char *
595 : 16 : get_diagnostic_text () const
596 : : {
597 : 16 : return pp_formatted_text (m_fmt.get_printer ());
598 : : }
599 : :
600 : : private:
601 : : const gcc_options m_opts;
602 : : test_diagnostic_context m_dc;
603 : : gcc::diagnostics_output_spec::context m_ctxt;
604 : : diagnostic_output_format &m_fmt;
605 : : };
606 : :
607 : : /* Selftests. */
608 : :
609 : : static void
610 : 4 : test_output_arg_parsing ()
611 : : {
612 : 4 : auto_fix_quotes fix_quotes;
613 : 4 : auto_fix_progname fix_progname;
614 : :
615 : : /* Minimal correct example. */
616 : 4 : {
617 : 4 : parser_test pt;
618 : 4 : auto result = pt.parse ("foo");
619 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
620 : 4 : ASSERT_EQ (result->m_kvs.size (), 0);
621 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
622 : 4 : }
623 : :
624 : : /* Stray trailing colon with no key/value pairs. */
625 : 4 : {
626 : 4 : parser_test pt;
627 : 4 : auto result = pt.parse ("foo:");
628 : 4 : ASSERT_EQ (result, nullptr);
629 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
630 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
631 : : "PROGNAME: error: `-fOPTION=foo:':"
632 : : " expected KEY=VALUE-style parameter for format `foo'"
633 : : " after `:';"
634 : : " got `'\n");
635 : 4 : }
636 : :
637 : : /* No key before '='. */
638 : 4 : {
639 : 4 : parser_test pt;
640 : 4 : auto result = pt.parse ("foo:=");
641 : 4 : ASSERT_EQ (result, nullptr);
642 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
643 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
644 : : "PROGNAME: error: `-fOPTION=foo:=':"
645 : : " expected KEY=VALUE-style parameter for format `foo'"
646 : : " after `:';"
647 : : " got `='\n");
648 : 4 : }
649 : :
650 : : /* No value for key. */
651 : 4 : {
652 : 4 : parser_test pt;
653 : 4 : auto result = pt.parse ("foo:key,");
654 : 4 : ASSERT_EQ (result, nullptr);
655 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
656 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
657 : : "PROGNAME: error: `-fOPTION=foo:key,':"
658 : : " expected KEY=VALUE-style parameter for format `foo'"
659 : : " after `:';"
660 : : " got `key,'\n");
661 : 4 : }
662 : :
663 : : /* Correct example, with one key/value pair. */
664 : 4 : {
665 : 4 : parser_test pt;
666 : 4 : auto result = pt.parse ("foo:key=value");
667 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
668 : 4 : ASSERT_EQ (result->m_kvs.size (), 1);
669 : 4 : ASSERT_EQ (result->m_kvs[0].first, "key");
670 : 4 : ASSERT_EQ (result->m_kvs[0].second, "value");
671 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
672 : 4 : }
673 : :
674 : : /* Stray trailing comma. */
675 : 4 : {
676 : 4 : parser_test pt;
677 : 4 : auto result = pt.parse ("foo:key=value,");
678 : 4 : ASSERT_EQ (result, nullptr);
679 : 4 : ASSERT_TRUE (pt.execution_failed_p ());
680 : 4 : ASSERT_STREQ (pt.get_diagnostic_text (),
681 : : "PROGNAME: error: `-fOPTION=foo:key=value,':"
682 : : " expected KEY=VALUE-style parameter for format `foo'"
683 : : " after `,';"
684 : : " got `'\n");
685 : 4 : }
686 : :
687 : : /* Correct example, with two key/value pairs. */
688 : 4 : {
689 : 4 : parser_test pt;
690 : 4 : auto result = pt.parse ("foo:color=red,shape=circle");
691 : 4 : ASSERT_EQ (result->m_scheme_name, "foo");
692 : 4 : ASSERT_EQ (result->m_kvs.size (), 2);
693 : 4 : ASSERT_EQ (result->m_kvs[0].first, "color");
694 : 4 : ASSERT_EQ (result->m_kvs[0].second, "red");
695 : 4 : ASSERT_EQ (result->m_kvs[1].first, "shape");
696 : 4 : ASSERT_EQ (result->m_kvs[1].second, "circle");
697 : 4 : ASSERT_FALSE (pt.execution_failed_p ());
698 : 4 : }
699 : 4 : }
700 : :
701 : : /* Run all of the selftests within this file. */
702 : :
703 : : void
704 : 4 : opts_diagnostic_cc_tests ()
705 : : {
706 : 4 : test_output_arg_parsing ();
707 : 4 : }
708 : :
709 : : } // namespace selftest
710 : :
711 : : #endif /* #if CHECKING_P */
|