[PATCH] RISC-V: Move UNSPEC_SSP_SET and UNSPEC_SSP_TEST to correct enum
[gcc.git] / gcc / opts-diagnostic.cc
blob6516e5aec7ebc63244c82b298ebe5de799ea0b36
1 /* Support for -fdiagnostics-add-output= and -fdiagnostics-set-output=.
2 Copyright (C) 2024-2025 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
21 /* This file implements the options -fdiagnostics-add-output=,
22 -fdiagnostics-set-output=, and their domain-specific language. */
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"
44 /* A namespace for handling the DSL of the arguments of
45 -fdiagnostics-add-output= and -fdiagnostics-set-output=. */
47 namespace gcc {
48 namespace diagnostics_output_spec {
50 /* Decls. */
52 struct context
54 public:
55 context (const gcc_options &opts,
56 diagnostic_context &dc,
57 line_maps *location_mgr,
58 location_t loc,
59 const char *option_name)
60 : m_opts (opts), m_dc (dc), m_location_mgr (location_mgr), m_loc (loc),
61 m_option_name (option_name)
64 void
65 report_error (const char *gmsgid, ...) const
66 ATTRIBUTE_GCC_DIAG(2,3);
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;
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;
80 diagnostic_output_file
81 open_output_file (label_text &&filename) const;
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;
90 struct scheme_name_and_params
92 std::string m_scheme_name;
93 std::vector<std::pair<std::string, std::string>> m_kvs;
96 static std::unique_ptr<scheme_name_and_params>
97 parse (const context &ctxt, const char *unparsed_arg);
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). */
103 class output_factory
105 public:
106 class scheme_handler
108 public:
109 scheme_handler (std::string scheme_name)
110 : m_scheme_name (std::move (scheme_name))
112 virtual ~scheme_handler () {}
114 const std::string &get_scheme_name () const { return m_scheme_name; }
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;
121 protected:
122 bool
123 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
129 if (value == "yes")
131 out = true;
132 return true;
134 else if (value == "no")
136 out = false;
137 return true;
139 else
141 ctxt.report_error
142 ("%<%s%s%>:"
143 " unexpected value %qs for key %qs; expected %qs or %qs",
144 ctxt.m_option_name, unparsed_arg,
145 value.c_str (),
146 key.c_str (),
147 "yes", "no");
149 return false;
152 template <typename EnumType, size_t NumValues>
153 bool
154 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
161 for (auto &iter : value_names)
162 if (value == iter.first)
164 out = iter.second;
165 return true;
168 auto_vec<const char *> known_values;
169 for (auto iter : value_names)
170 known_values.safe_push (iter.first);
171 pp_markup::comma_separated_quoted_strings e (known_values);
172 ctxt.report_error
173 ("%<%s%s%>:"
174 " unexpected value %qs for key %qs; known values: %e",
175 ctxt.m_option_name, unparsed_arg,
176 value.c_str (),
177 key.c_str (),
178 &e);
179 return false;
182 private:
183 const std::string m_scheme_name;
186 output_factory ();
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);
193 const scheme_handler *get_scheme_handler (const std::string &scheme_name);
195 private:
196 std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
199 class text_scheme_handler : public output_factory::scheme_handler
201 public:
202 text_scheme_handler () : scheme_handler ("text") {}
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;
210 class sarif_scheme_handler : public output_factory::scheme_handler
212 public:
213 sarif_scheme_handler () : scheme_handler ("sarif") {}
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;
221 /* struct context. */
223 void
224 context::report_error (const char *gmsgid, ...) const
226 m_dc.begin_group ();
227 va_list ap;
228 va_start (ap, gmsgid);
229 rich_location richloc (m_location_mgr, m_loc);
230 m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap, DK_ERROR);
231 va_end (ap);
232 m_dc.end_group ();
235 void
236 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
241 pp_markup::comma_separated_quoted_strings e (known_keys);
242 report_error
243 ("%<%s%s%>:"
244 " unknown key %qs for format %qs; known keys: %e",
245 m_option_name, unparsed_arg,
246 key.c_str (), scheme_name.c_str (), &e);
249 void
250 context::report_missing_key (const char *unparsed_arg,
251 const std::string &key,
252 const std::string &scheme_name,
253 const char *metavar) const
255 report_error
256 ("%<%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 m_option_name, scheme_name.c_str (), key.c_str (), metavar);
264 std::unique_ptr<scheme_name_and_params>
265 parse (const context &ctxt, const char *unparsed_arg)
267 scheme_name_and_params result;
268 if (const char *const colon = strchr (unparsed_arg, ':'))
270 result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
271 /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
272 const char *iter = colon + 1;
273 const char *last_separator = ":";
274 while (iter)
276 /* Look for a non-empty key string followed by '='. */
277 const char *eq = strchr (iter, '=');
278 if (eq == nullptr || eq == iter)
280 /* Missing '='. */
281 ctxt.report_error
282 ("%<%s%s%>:"
283 " expected KEY=VALUE-style parameter for format %qs"
284 " after %qs;"
285 " got %qs",
286 ctxt.m_option_name, unparsed_arg,
287 result.m_scheme_name.c_str (),
288 last_separator,
289 iter);
290 return nullptr;
292 std::string key = std::string (iter, eq - iter);
293 std::string value;
294 const char *comma = strchr (iter, ',');
295 if (comma)
297 value = std::string (eq + 1, comma - (eq + 1));
298 iter = comma + 1;
299 last_separator = ",";
301 else
303 value = std::string (eq + 1);
304 iter = nullptr;
306 result.m_kvs.push_back ({std::move (key), std::move (value)});
309 else
310 result.m_scheme_name = unparsed_arg;
311 return ::make_unique<scheme_name_and_params> (std::move (result));
314 /* class output_factory::scheme_handler. */
316 /* class output_factory. */
318 output_factory::output_factory ()
320 m_scheme_handlers.push_back (::make_unique<text_scheme_handler> ());
321 m_scheme_handlers.push_back (::make_unique<sarif_scheme_handler> ());
324 const output_factory::scheme_handler *
325 output_factory::get_scheme_handler (const std::string &scheme_name)
327 for (auto &iter : m_scheme_handlers)
328 if (iter->get_scheme_name () == scheme_name)
329 return iter.get ();
330 return nullptr;
333 std::unique_ptr<diagnostic_output_format>
334 output_factory::make_sink (const context &ctxt,
335 const char *unparsed_arg,
336 const scheme_name_and_params &parsed_arg)
338 auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
339 if (!scheme_handler)
341 auto_vec<const char *> strings;
342 for (auto &iter : m_scheme_handlers)
343 strings.safe_push (iter->get_scheme_name ().c_str ());
344 pp_markup::comma_separated_quoted_strings e (strings);
345 ctxt.report_error ("%<%s%s%>:"
346 " unrecognized format %qs; known formats: %e",
347 ctxt.m_option_name, unparsed_arg,
348 parsed_arg.m_scheme_name.c_str (), &e);
349 return nullptr;
352 return scheme_handler->make_sink (ctxt, unparsed_arg, parsed_arg);
355 /* class text_scheme_handler : public output_factory::scheme_handler. */
357 std::unique_ptr<diagnostic_output_format>
358 text_scheme_handler::make_sink (const context &ctxt,
359 const char *unparsed_arg,
360 const scheme_name_and_params &parsed_arg) const
362 bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
363 bool show_nesting = false;
364 bool show_locations_in_nesting = true;
365 bool show_levels = false;
366 for (auto& iter : parsed_arg.m_kvs)
368 const std::string &key = iter.first;
369 const std::string &value = iter.second;
370 if (key == "color")
372 if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
373 return nullptr;
374 continue;
376 if (key == "experimental-nesting")
378 if (!parse_bool_value (ctxt, unparsed_arg, key, value,
379 show_nesting))
380 return nullptr;
381 continue;
383 if (key == "experimental-nesting-show-locations")
385 if (!parse_bool_value (ctxt, unparsed_arg, key, value,
386 show_locations_in_nesting))
387 return nullptr;
388 continue;
390 if (key == "experimental-nesting-show-levels")
392 if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
393 return nullptr;
394 continue;
397 /* Key not found. */
398 auto_vec<const char *> known_keys;
399 known_keys.safe_push ("color");
400 known_keys.safe_push ("experimental-nesting");
401 known_keys.safe_push ("experimental-nesting-show-locations");
402 known_keys.safe_push ("experimental-nesting-show-levels");
403 ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
404 known_keys);
405 return nullptr;
408 auto sink = ::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
409 sink->set_show_nesting (show_nesting);
410 sink->set_show_locations_in_nesting (show_locations_in_nesting);
411 sink->set_show_nesting_levels (show_levels);
412 return sink;
415 diagnostic_output_file
416 context::open_output_file (label_text &&filename) const
418 FILE *outf = fopen (filename.get (), "w");
419 if (!outf)
421 rich_location richloc (m_location_mgr, m_loc);
422 m_dc.emit_diagnostic_with_group
423 (DK_ERROR, richloc, nullptr, 0,
424 "unable to open %qs: %m", filename.get ());
425 return diagnostic_output_file (nullptr, false, std::move (filename));
427 return diagnostic_output_file (outf, true, std::move (filename));
430 /* class sarif_scheme_handler : public output_factory::scheme_handler. */
432 std::unique_ptr<diagnostic_output_format>
433 sarif_scheme_handler::make_sink (const context &ctxt,
434 const char *unparsed_arg,
435 const scheme_name_and_params &parsed_arg) const
437 enum sarif_version version = sarif_version::v2_1_0;
438 label_text filename;
439 for (auto& iter : parsed_arg.m_kvs)
441 const std::string &key = iter.first;
442 const std::string &value = iter.second;
443 if (key == "version")
445 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}}};
450 if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
451 key, value,
452 value_names,
453 version))
454 return nullptr;
455 continue;
457 if (key == "file")
459 filename = label_text::take (xstrdup (value.c_str ()));
460 continue;
463 /* Key not found. */
464 auto_vec<const char *> known_keys;
465 known_keys.safe_push ("file");
466 known_keys.safe_push ("version");
467 ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
468 known_keys);
469 return nullptr;
472 diagnostic_output_file output_file;
473 if (filename.get ())
474 output_file = ctxt.open_output_file (std::move (filename));
475 else
476 // Default filename
478 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 output_file = diagnostic_output_format_open_sarif_file (ctxt.m_dc,
482 line_table,
483 basename);
485 if (!output_file)
486 return nullptr;
488 auto sink = make_sarif_sink (ctxt.m_dc,
489 *line_table,
490 ctxt.m_opts.x_main_input_filename,
491 version,
492 std::move (output_file));
493 return sink;
496 } // namespace diagnostics_output_spec
497 } // namespace gcc
499 void
500 handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
501 diagnostic_context &dc,
502 const char *arg,
503 location_t loc)
505 gcc_assert (arg);
506 gcc_assert (line_table);
508 const char *const option_name = "-fdiagnostics-add-output=";
509 gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
510 option_name);
511 auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
512 if (!result)
513 return;
515 gcc::diagnostics_output_spec::output_factory factory;
516 auto sink = factory.make_sink (ctxt, arg, *result);
517 if (!sink)
518 return;
520 dc.add_sink (std::move (sink));
523 void
524 handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
525 diagnostic_context &dc,
526 const char *arg,
527 location_t loc)
529 gcc_assert (arg);
530 gcc_assert (line_table);
532 const char *const option_name = "-fdiagnostics-set-output=";
533 gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
534 option_name);
535 auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
536 if (!result)
537 return;
539 gcc::diagnostics_output_spec::output_factory factory;
540 auto sink = factory.make_sink (ctxt, arg, *result);
541 if (!sink)
542 return;
544 dc.set_output_format (std::move (sink));
547 #if CHECKING_P
549 namespace selftest {
551 /* RAII class to temporarily override "progname" to the
552 string "PROGNAME". */
554 class auto_fix_progname
556 public:
557 auto_fix_progname ()
559 m_old_progname = progname;
560 progname = "PROGNAME";
563 ~auto_fix_progname ()
565 progname = m_old_progname;
568 private:
569 const char *m_old_progname;
572 struct parser_test
574 parser_test ()
575 : m_opts (),
576 m_dc (),
577 m_ctxt (m_opts, m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
578 m_fmt (m_dc.get_output_format (0))
580 pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
583 std::unique_ptr<gcc::diagnostics_output_spec::scheme_name_and_params>
584 parse (const char *unparsed_arg)
586 return gcc::diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
589 bool execution_failed_p () const
591 return m_dc.execution_failed_p ();
594 const char *
595 get_diagnostic_text () const
597 return pp_formatted_text (m_fmt.get_printer ());
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;
607 /* Selftests. */
609 static void
610 test_output_arg_parsing ()
612 auto_fix_quotes fix_quotes;
613 auto_fix_progname fix_progname;
615 /* Minimal correct example. */
617 parser_test pt;
618 auto result = pt.parse ("foo");
619 ASSERT_EQ (result->m_scheme_name, "foo");
620 ASSERT_EQ (result->m_kvs.size (), 0);
621 ASSERT_FALSE (pt.execution_failed_p ());
624 /* Stray trailing colon with no key/value pairs. */
626 parser_test pt;
627 auto result = pt.parse ("foo:");
628 ASSERT_EQ (result, nullptr);
629 ASSERT_TRUE (pt.execution_failed_p ());
630 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");
637 /* No key before '='. */
639 parser_test pt;
640 auto result = pt.parse ("foo:=");
641 ASSERT_EQ (result, nullptr);
642 ASSERT_TRUE (pt.execution_failed_p ());
643 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");
650 /* No value for key. */
652 parser_test pt;
653 auto result = pt.parse ("foo:key,");
654 ASSERT_EQ (result, nullptr);
655 ASSERT_TRUE (pt.execution_failed_p ());
656 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");
663 /* Correct example, with one key/value pair. */
665 parser_test pt;
666 auto result = pt.parse ("foo:key=value");
667 ASSERT_EQ (result->m_scheme_name, "foo");
668 ASSERT_EQ (result->m_kvs.size (), 1);
669 ASSERT_EQ (result->m_kvs[0].first, "key");
670 ASSERT_EQ (result->m_kvs[0].second, "value");
671 ASSERT_FALSE (pt.execution_failed_p ());
674 /* Stray trailing comma. */
676 parser_test pt;
677 auto result = pt.parse ("foo:key=value,");
678 ASSERT_EQ (result, nullptr);
679 ASSERT_TRUE (pt.execution_failed_p ());
680 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");
687 /* Correct example, with two key/value pairs. */
689 parser_test pt;
690 auto result = pt.parse ("foo:color=red,shape=circle");
691 ASSERT_EQ (result->m_scheme_name, "foo");
692 ASSERT_EQ (result->m_kvs.size (), 2);
693 ASSERT_EQ (result->m_kvs[0].first, "color");
694 ASSERT_EQ (result->m_kvs[0].second, "red");
695 ASSERT_EQ (result->m_kvs[1].first, "shape");
696 ASSERT_EQ (result->m_kvs[1].second, "circle");
697 ASSERT_FALSE (pt.execution_failed_p ());
701 /* Run all of the selftests within this file. */
703 void
704 opts_diagnostic_cc_tests ()
706 test_output_arg_parsing ();
709 } // namespace selftest
711 #endif /* #if CHECKING_P */