diff mbox series

[01/12] diagnostics: add ability to associate diagnostics with rules from coding standards

Message ID 20220622223447.2462880-2-dmalcolm@redhat.com
State New
Headers show
Series RFC: Replay of serialized diagnostics | expand

Commit Message

David Malcolm June 22, 2022, 10:34 p.m. UTC
gcc/ChangeLog:
	* common.opt (fdiagnostics-show-rules): New option.
	* diagnostic-format-json.cc (diagnostic_output_format_init_json):
	Fix up context->show_rules.
	* diagnostic-format-sarif.cc
	(diagnostic_output_format_init_sarif): Likewise.
	* diagnostic-metadata.h (diagnostic_metadata::rule): New class.
	(diagnostic_metadata::precanned_rule): New class.
	(diagnostic_metadata::add_rule): New.
	(diagnostic_metadata::get_num_rules): New.
	(diagnostic_metadata::get_rule): New.
	(diagnostic_metadata::m_rules): New field.
	* diagnostic.cc (diagnostic_initialize): Initialize show_rules.
	(print_any_rules): New.
	(diagnostic_report_diagnostic): Call it.
	* diagnostic.h (diagnostic_context::show_rules): New field.
	* doc/invoke.texi (-fno-diagnostics-show-rules): New option.
	* opts.cc (common_handle_option): Handle
	OPT_fdiagnostics_show_rules.
	* toplev.cc (general_init): Set up global_dc->show_rules.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/diagnostic-test-metadata.c: Expect " [STR34-C]" to
	be emitted at the "gets" call.
	* gcc.dg/plugin/diagnostic_plugin_test_metadata.c
	(pass_test_metadata::execute): Associate the "gets" diagnostic
	with a rule named "STR34-C".

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/common.opt                                |  4 ++
 gcc/diagnostic-format-json.cc                 |  1 +
 gcc/diagnostic-format-sarif.cc                |  1 +
 gcc/diagnostic-metadata.h                     | 47 +++++++++++++++++-
 gcc/diagnostic.cc                             | 48 +++++++++++++++++++
 gcc/diagnostic.h                              |  3 ++
 gcc/doc/invoke.texi                           | 10 ++++
 gcc/opts.cc                                   |  4 ++
 .../gcc.dg/plugin/diagnostic-test-metadata.c  |  2 +-
 .../plugin/diagnostic_plugin_test_metadata.c  |  9 +++-
 gcc/toplev.cc                                 |  2 +
 11 files changed, 127 insertions(+), 4 deletions(-)

Comments

David Malcolm June 23, 2022, 7:04 p.m. UTC | #1
On Wed, 2022-06-22 at 18:34 -0400, David Malcolm wrote:
> gcc/ChangeLog:
>         * common.opt (fdiagnostics-show-rules): New option.
>         * diagnostic-format-json.cc
> (diagnostic_output_format_init_json):
>         Fix up context->show_rules.
>         * diagnostic-format-sarif.cc
>         (diagnostic_output_format_init_sarif): Likewise.
>         * diagnostic-metadata.h (diagnostic_metadata::rule): New
> class.
>         (diagnostic_metadata::precanned_rule): New class.
>         (diagnostic_metadata::add_rule): New.
>         (diagnostic_metadata::get_num_rules): New.
>         (diagnostic_metadata::get_rule): New.
>         (diagnostic_metadata::m_rules): New field.
>         * diagnostic.cc (diagnostic_initialize): Initialize
> show_rules.
>         (print_any_rules): New.
>         (diagnostic_report_diagnostic): Call it.
>         * diagnostic.h (diagnostic_context::show_rules): New field.
>         * doc/invoke.texi (-fno-diagnostics-show-rules): New option.
>         * opts.cc (common_handle_option): Handle
>         OPT_fdiagnostics_show_rules.
>         * toplev.cc (general_init): Set up global_dc->show_rules.
> 
> gcc/testsuite/ChangeLog:
>         * gcc.dg/plugin/diagnostic-test-metadata.c: Expect " [STR34-
> C]" to
>         be emitted at the "gets" call.
>         * gcc.dg/plugin/diagnostic_plugin_test_metadata.c
>         (pass_test_metadata::execute): Associate the "gets"
> diagnostic
>         with a rule named "STR34-C".
> 
> Signed-off-by: David Malcolm <dmalcolm@redhat.com>

This one seems potentially useful to plugin authors without the rest of
the patch kit, so I've pushed it to trunk as: r13-1221-g0b14f590e3e9d9
(after a successful bootstrap & regression test on x86_64-pc-linux-gnu)

Dave
diff mbox series

Patch

diff --git a/gcc/common.opt b/gcc/common.opt
index 32917aafcae..3a842847a74 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1466,6 +1466,10 @@  fdiagnostics-show-cwe
 Common Var(flag_diagnostics_show_cwe) Init(1)
 Print CWE identifiers for diagnostic messages, where available.
 
+fdiagnostics-show-rules
+Common Var(flag_diagnostics_show_rules) Init(1)
+Print any rules associated with diagnostic messages.
+
 fdiagnostics-path-format=
 Common Joined RejectNegative Var(flag_diagnostics_path_format) Enum(diagnostic_path_format) Init(DPF_INLINE_EVENTS)
 Specify how to print any control-flow path associated with a diagnostic.
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index 051fa6c2e48..d1d8d3f2081 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -345,6 +345,7 @@  diagnostic_output_format_init_json (diagnostic_context *context)
 
   /* The metadata is handled in JSON format, rather than as text.  */
   context->show_cwe = false;
+  context->show_rules = false;
 
   /* The option is handled in JSON format, rather than as text.  */
   context->show_option_requested = false;
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 0c33179e8cf..a7bb9fb639d 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -1556,6 +1556,7 @@  diagnostic_output_format_init_sarif (diagnostic_context *context)
 
   /* The metadata is handled in SARIF format, rather than as text.  */
   context->show_cwe = false;
+  context->show_rules = false;
 
   /* The option is handled in SARIF format, rather than as text.  */
   context->show_option_requested = false;
diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h
index ae59942c65e..80017d35fa9 100644
--- a/gcc/diagnostic-metadata.h
+++ b/gcc/diagnostic-metadata.h
@@ -24,19 +24,62 @@  along with GCC; see the file COPYING3.  If not see
 /* A bundle of additional metadata that can be associated with a
    diagnostic.
 
-   Currently this only supports associating a CWE identifier with a
-   diagnostic.  */
+   This supports an optional CWE identifier, and zero or more
+   "rules".  */
 
 class diagnostic_metadata
 {
  public:
+  /* Abstract base class for referencing a rule that has been violated,
+     such as within a coding standard, or within a specification.  */
+  class rule
+  {
+  public:
+    virtual char *make_description () const = 0;
+    virtual char *make_url () const = 0;
+  };
+
+  /* Concrete subclass.  */
+  class precanned_rule : public rule
+  {
+  public:
+    precanned_rule (const char *desc, const char *url)
+    : m_desc (desc), m_url (url)
+    {}
+
+    char *make_description () const final override
+    {
+      return m_desc ? xstrdup (m_desc) : NULL;
+    }
+
+    char *make_url () const final override
+    {
+      return m_url ? xstrdup (m_url) : NULL;
+    }
+
+  private:
+    const char *m_desc;
+    const char *m_url;
+  };
+
   diagnostic_metadata () : m_cwe (0) {}
 
   void add_cwe (int cwe) { m_cwe = cwe; }
   int get_cwe () const { return m_cwe; }
 
+  /* Associate R with the diagnostic.  R must outlive
+     the metadata.  */
+  void add_rule (const rule &r)
+  {
+    m_rules.safe_push (&r);
+  }
+
+  unsigned get_num_rules () const { return m_rules.length (); }
+  const rule &get_rule (unsigned idx) const { return *(m_rules[idx]); }
+
  private:
   int m_cwe;
+  auto_vec<const rule *> m_rules;
 };
 
 #endif /* ! GCC_DIAGNOSTIC_METADATA_H */
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index f2a82fff462..22f7b0b6d6e 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -190,6 +190,7 @@  diagnostic_initialize (diagnostic_context *context, int n_opts)
   for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
     context->caret_chars[i] = '^';
   context->show_cwe = false;
+  context->show_rules = false;
   context->path_format = DPF_NONE;
   context->show_path_depths = false;
   context->show_option_requested = false;
@@ -1291,6 +1292,51 @@  print_any_cwe (diagnostic_context *context,
     }
 }
 
+/* If DIAGNOSTIC has any rules associated with it, print them.
+
+   For example, if the diagnostic metadata associates it with a rule
+   named "STR34-C", then " [STR34-C]" will be printed, suitably colorized,
+   with any URL provided by the rule.  */
+
+static void
+print_any_rules (diagnostic_context *context,
+		const diagnostic_info *diagnostic)
+{
+  if (diagnostic->metadata == NULL)
+    return;
+
+  for (unsigned idx = 0; idx < diagnostic->metadata->get_num_rules (); idx++)
+    {
+      const diagnostic_metadata::rule &rule
+	= diagnostic->metadata->get_rule (idx);
+      if (char *desc = rule.make_description ())
+	{
+	  pretty_printer *pp = context->printer;
+	  char *saved_prefix = pp_take_prefix (context->printer);
+	  pp_string (pp, " [");
+	  pp_string (pp,
+		     colorize_start (pp_show_color (pp),
+				     diagnostic_kind_color[diagnostic->kind]));
+	  char *url = NULL;
+	  if (pp->url_format != URL_FORMAT_NONE)
+	    {
+	      url = rule.make_url ();
+	      if (url)
+		pp_begin_url (pp, url);
+	    }
+	  pp_string (pp, desc);
+	  pp_set_prefix (context->printer, saved_prefix);
+	  if (pp->url_format != URL_FORMAT_NONE)
+	    if (url)
+	      pp_end_url (pp);
+	  free (url);
+	  pp_string (pp, colorize_stop (pp_show_color (pp)));
+	  pp_character (pp, ']');
+	  free (desc);
+	}
+    }
+}
+
 /* Print any metadata about the option used to control DIAGNOSTIC to CONTEXT's
    printer, e.g. " [-Werror=uninitialized]".
    Subroutine of diagnostic_report_diagnostic.  */
@@ -1504,6 +1550,8 @@  diagnostic_report_diagnostic (diagnostic_context *context,
   pp_output_formatted_text (context->printer);
   if (context->show_cwe)
     print_any_cwe (context, diagnostic);
+  if (context->show_rules)
+    print_any_rules (context, diagnostic);
   if (context->show_option_requested)
     print_option_information (context, diagnostic, orig_diag_kind);
   (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 96c9a7202f9..ae6f2dfb7f4 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -227,6 +227,9 @@  struct diagnostic_context
      diagnostics.  */
   bool show_cwe;
 
+  /* True if we should print any rules associated with diagnostics.  */
+  bool show_rules;
+
   /* How should diagnostic_path objects be printed.  */
   enum diagnostic_path_format path_format;
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 50f57877477..4bd73c197e7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -305,6 +305,7 @@  Objective-C and Objective-C++ Dialects}.
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
 -fno-diagnostics-show-labels  -fno-diagnostics-show-line-numbers @gol
 -fno-diagnostics-show-cwe  @gol
+-fno-diagnostics-show-rule  @gol
 -fdiagnostics-minimum-margin-width=@var{width} @gol
 -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
 -fdiagnostics-show-template-tree  -fno-elide-type @gol
@@ -5028,6 +5029,15 @@  diagnostics.  GCC plugins may also provide diagnostics with such metadata.
 By default, if this information is present, it will be printed with
 the diagnostic.  This option suppresses the printing of this metadata.
 
+@item -fno-diagnostics-show-rules
+@opindex fno-diagnostics-show-rules
+@opindex fdiagnostics-show-rules
+Diagnostic messages can optionally have rules associated with them, such
+as from a coding standard, or a specification.
+GCC itself does not do this for any of its diagnostics, but plugins may do so.
+By default, if this information is present, it will be printed with
+the diagnostic.  This option suppresses the printing of this metadata.
+
 @item -fno-diagnostics-show-line-numbers
 @opindex fno-diagnostics-show-line-numbers
 @opindex fdiagnostics-show-line-numbers
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 959d48d173f..ef485455093 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2872,6 +2872,10 @@  common_handle_option (struct gcc_options *opts,
       dc->show_cwe = value;
       break;
 
+    case OPT_fdiagnostics_show_rules:
+      dc->show_rules = value;
+      break;
+
     case OPT_fdiagnostics_path_format_:
       dc->path_format = (enum diagnostic_path_format)value;
       break;
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c
index d2babd35753..38ecf0a6d95 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata.c
@@ -5,5 +5,5 @@  extern char *gets (char *s);
 void test_cwe (void)
 {
   char buf[1024];
-  gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\]" } */
+  gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\] \\\[STR34-C\\\]" } */
 }
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c
index 4b13afc093d..b86a8b3650e 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_metadata.c
@@ -106,9 +106,16 @@  pass_test_metadata::execute (function *fun)
 	if (gcall *call = check_for_named_call (stmt, "gets", 1))
 	  {
 	    gcc_rich_location richloc (gimple_location (call));
-	    /* CWE-242: Use of Inherently Dangerous Function.  */
 	    diagnostic_metadata m;
+
+	    /* CWE-242: Use of Inherently Dangerous Function.  */
 	    m.add_cwe (242);
+
+	    /* Example of a diagnostic_metadata::rule.  */
+	    diagnostic_metadata::precanned_rule
+	      test_rule ("STR34-C", "https://example.com/");
+	    m.add_rule (test_rule);
+
 	    warning_meta (&richloc, m, 0,
 			  "never use %qs", "gets");
 	  }
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 055e0642f77..a24ad5db438 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1038,6 +1038,8 @@  general_init (const char *argv0, bool init_signals)
     = global_options_init.x_flag_diagnostics_show_line_numbers;
   global_dc->show_cwe
     = global_options_init.x_flag_diagnostics_show_cwe;
+  global_dc->show_rules
+    = global_options_init.x_flag_diagnostics_show_rules;
   global_dc->path_format
     = (enum diagnostic_path_format)global_options_init.x_flag_diagnostics_path_format;
   global_dc->show_path_depths