diff mbox series

[11/49] Add diagnostic_metadata and CWE support

Message ID 1573867416-55618-12-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series RFC: Add a static analysis framework to GCC | expand

Commit Message

David Malcolm Nov. 16, 2019, 1:22 a.m. UTC
This patch adds support for associating a diagnostic with an optional
diagnostic_metadata object, so that plugins can add extra data to their
diagnostics (e.g. mapping a diagnostic to a taxonomy or coding standard
such as from CERT or MISRA).

Currently this only supports associating a CWE identifier with a
diagnostic (which is what I'm using for the analyzer warnings later in
the patch kit), but adding a diagnostic_metadata class allows for future
growth in this area without an explosion of further "warning_at" overloads
for all of the different kinds of custom data that a plugin might want to
add.

gcc/ChangeLog:
	* common.opt (fdiagnostics-show-metadata): New option.
	* diagnostic-core.h (class diagnostic_metadata): New forward decl.
	(warning_at): Add overload taking a const diagnostic_metadata &.
	(emit_diagnostic_valist): Add overload taking a
	const diagnostic_metadata *.
	* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
	(json_from_metadata): New function.
	(json_end_diagnostic): Call it to add "metadata" child for
	diagnostics with metadata.
	(diagnostic_output_format_init): Clear context->show_metadata.
	* diagnostic-metadata.h: New file.
	* diagnostic.c: Include "diagnostic-metadata.h".
	(diagnostic_impl): Add const diagnostic_metadata * param.
	(diagnostic_n_impl): Likewise.
	(diagnostic_initialize): Initialize context->show_metadata.
	(diagnostic_set_info_translated): Initialize diagnostic->metadata.
	(get_cwe_url): New function.
	(print_any_metadata): New function.
	(diagnostic_report_diagnostic): Call print_any_metadata if the
	diagnostic has non-NULL metadata.
	(emit_diagnostic): Pass NULL as the metadata in the call to
	diagnostic_impl.
	(emit_diagnostic_valist): Likewise.
	(emit_diagnostic_valist): New overload taking a
	const diagnostic_metadata *.
	(inform): Pass NULL as the metadata in the call to
	diagnostic_impl.
	(inform_n): Likewise for diagnostic_n_impl.
	(warning): Likewise.
	(warning_at): Likewise.  Add overload that takes a
	const diagnostic_metadata &.
	(warning_n): Pass NULL as the metadata in the call to
	diagnostic_n_impl.
	(pedwarn): Likewise for diagnostic_impl.
	(permerror): Likewise.
	(error): Likewise.
	(error_n): Likewise.
	(error_at): Likewise.
	(sorry): Likewise.
	(sorry_at): Likewise.
	(fatal_error): Likewise.
	(internal_error): Likewise.
	(internal_error_no_backtrace): Likewise.
	* diagnostic.h (diagnostic_info::metadata): New field.
	(diagnostic_context::show_metadata): New field.
	* doc/invoke.texi (-fno-diagnostics-show-metadata): New option.
	* opts.c (common_handle_option): Handle
	OPT_fdiagnostics_show_metadata.
	* toplev.c (general_init): Initialize global_dc->show_metadata.
---
 gcc/common.opt                |   4 ++
 gcc/diagnostic-core.h         |  10 +++
 gcc/diagnostic-format-json.cc |  23 +++++++
 gcc/diagnostic-metadata.h     |  42 +++++++++++++
 gcc/diagnostic.c              | 142 +++++++++++++++++++++++++++++++++---------
 gcc/diagnostic.h              |   7 +++
 gcc/doc/invoke.texi           |   8 +++
 gcc/opts.c                    |   4 ++
 gcc/toplev.c                  |   2 +
 9 files changed, 211 insertions(+), 31 deletions(-)
 create mode 100644 gcc/diagnostic-metadata.h

Comments

Martin Sebor Dec. 4, 2019, 5:36 p.m. UTC | #1
On 11/15/19 6:22 PM, David Malcolm wrote:
> This patch adds support for associating a diagnostic with an optional
> diagnostic_metadata object, so that plugins can add extra data to their
> diagnostics (e.g. mapping a diagnostic to a taxonomy or coding standard
> such as from CERT or MISRA).
> 
> Currently this only supports associating a CWE identifier with a
> diagnostic (which is what I'm using for the analyzer warnings later in
> the patch kit), but adding a diagnostic_metadata class allows for future
> growth in this area without an explosion of further "warning_at" overloads
> for all of the different kinds of custom data that a plugin might want to
> add.

We discussed this in the past so I'll be repeating some of my
comments from that thread.  I like the feature at a high level,
but I'm not sure I see how to make it work in a robust way.

There is no one-to-one mapping between GCC warnings and any of
these classifications.  A single GCC warning might map to several
CERT or MISRA guidelines, and a single guideline might map to two
or more different GCC warnings.  This is an M to N mapping times
the number of coding standards/classifications.  I haven't looked
at the patch kit in enough detail to understand how it tries to
handle it.  Can you explain?

My other concern here is that these guidelines evolve.  New ones
are being added, existing ones moved or broken up, etc.  Does
the infrastructure cope with this evolution (e.g., by reading
the mapping from a file)?

Martin

> 
> gcc/ChangeLog:
> 	* common.opt (fdiagnostics-show-metadata): New option.
> 	* diagnostic-core.h (class diagnostic_metadata): New forward decl.
> 	(warning_at): Add overload taking a const diagnostic_metadata &.
> 	(emit_diagnostic_valist): Add overload taking a
> 	const diagnostic_metadata *.
> 	* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
> 	(json_from_metadata): New function.
> 	(json_end_diagnostic): Call it to add "metadata" child for
> 	diagnostics with metadata.
> 	(diagnostic_output_format_init): Clear context->show_metadata.
> 	* diagnostic-metadata.h: New file.
> 	* diagnostic.c: Include "diagnostic-metadata.h".
> 	(diagnostic_impl): Add const diagnostic_metadata * param.
> 	(diagnostic_n_impl): Likewise.
> 	(diagnostic_initialize): Initialize context->show_metadata.
> 	(diagnostic_set_info_translated): Initialize diagnostic->metadata.
> 	(get_cwe_url): New function.
> 	(print_any_metadata): New function.
> 	(diagnostic_report_diagnostic): Call print_any_metadata if the
> 	diagnostic has non-NULL metadata.
> 	(emit_diagnostic): Pass NULL as the metadata in the call to
> 	diagnostic_impl.
> 	(emit_diagnostic_valist): Likewise.
> 	(emit_diagnostic_valist): New overload taking a
> 	const diagnostic_metadata *.
> 	(inform): Pass NULL as the metadata in the call to
> 	diagnostic_impl.
> 	(inform_n): Likewise for diagnostic_n_impl.
> 	(warning): Likewise.
> 	(warning_at): Likewise.  Add overload that takes a
> 	const diagnostic_metadata &.
> 	(warning_n): Pass NULL as the metadata in the call to
> 	diagnostic_n_impl.
> 	(pedwarn): Likewise for diagnostic_impl.
> 	(permerror): Likewise.
> 	(error): Likewise.
> 	(error_n): Likewise.
> 	(error_at): Likewise.
> 	(sorry): Likewise.
> 	(sorry_at): Likewise.
> 	(fatal_error): Likewise.
> 	(internal_error): Likewise.
> 	(internal_error_no_backtrace): Likewise.
> 	* diagnostic.h (diagnostic_info::metadata): New field.
> 	(diagnostic_context::show_metadata): New field.
> 	* doc/invoke.texi (-fno-diagnostics-show-metadata): New option.
> 	* opts.c (common_handle_option): Handle
> 	OPT_fdiagnostics_show_metadata.
> 	* toplev.c (general_init): Initialize global_dc->show_metadata.
> ---
>   gcc/common.opt                |   4 ++
>   gcc/diagnostic-core.h         |  10 +++
>   gcc/diagnostic-format-json.cc |  23 +++++++
>   gcc/diagnostic-metadata.h     |  42 +++++++++++++
>   gcc/diagnostic.c              | 142 +++++++++++++++++++++++++++++++++---------
>   gcc/diagnostic.h              |   7 +++
>   gcc/doc/invoke.texi           |   8 +++
>   gcc/opts.c                    |   4 ++
>   gcc/toplev.c                  |   2 +
>   9 files changed, 211 insertions(+), 31 deletions(-)
>   create mode 100644 gcc/diagnostic-metadata.h
> 
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 3c024b3..228df32 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1334,6 +1334,10 @@ fdiagnostics-show-option
>   Common Var(flag_diagnostics_show_option) Init(1)
>   Amend appropriate diagnostic messages with the command line option that controls them.
>   
> +fdiagnostics-show-metadata
> +Common Var(flag_diagnostics_show_metadata) Init(1)
> +Print additional metadata for diagnostic messages, where available.
> +
>   fdiagnostics-minimum-margin-width=
>   Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
>   Set minimum width of left margin of source code when showing source.
> diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
> index efafde4..2e7f120 100644
> --- a/gcc/diagnostic-core.h
> +++ b/gcc/diagnostic-core.h
> @@ -45,6 +45,9 @@ class auto_diagnostic_group
>     ~auto_diagnostic_group ();
>   };
>   
> +/* Forward decl.  */
> +class diagnostic_metadata; /* See diagnostic-metadata.h.  */
> +
>   extern const char *progname;
>   
>   extern const char *trim_filename (const char *);
> @@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const char *, ...)
>       ATTRIBUTE_GCC_DIAG(3,4);
>   extern bool warning_at (rich_location *, int, const char *, ...)
>       ATTRIBUTE_GCC_DIAG(3,4);
> +extern bool warning_at (rich_location *, const diagnostic_metadata &, int,
> +			const char *, ...)
> +    ATTRIBUTE_GCC_DIAG(4,5);
>   extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
>   extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
>   		     const char *, ...)
> @@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
>   			     const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
>   extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
>   				    va_list *) ATTRIBUTE_GCC_DIAG (4,0);
> +extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
> +				    const diagnostic_metadata *metadata,
> +				    int, const char *, va_list *)
> +  ATTRIBUTE_GCC_DIAG (5,0);
>   extern bool seen_error (void);
>   
>   #ifdef BUFSIZ
> diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
> index eb99952..200e844 100644
> --- a/gcc/diagnostic-format-json.cc
> +++ b/gcc/diagnostic-format-json.cc
> @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "system.h"
>   #include "coretypes.h"
>   #include "diagnostic.h"
> +#include "diagnostic-metadata.h"
>   #include "json.h"
>   #include "selftest.h"
>   
> @@ -103,6 +104,19 @@ json_from_fixit_hint (const fixit_hint *hint)
>     return fixit_obj;
>   }
>   
> +/* Generate a JSON object for METADATA.  */
> +
> +static json::object *
> +json_from_metadata (const diagnostic_metadata *metadata)
> +{
> +  json::object *metadata_obj = new json::object ();
> +
> +  if (metadata->get_cwe ())
> +    metadata_obj->set ("cwe", new json::number (metadata->get_cwe ()));
> +
> +  return metadata_obj;
> +}
> +
>   /* No-op implementation of "begin_diagnostic" for JSON output.  */
>   
>   static void
> @@ -211,6 +225,12 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
>        TODO: functions
>        TODO: inlining information
>        TODO: macro expansion information.  */
> +
> +  if (diagnostic->metadata)
> +    {
> +      json::object *metadata_obj = json_from_metadata (diagnostic->metadata);
> +      diag_obj->set ("metadata", metadata_obj);
> +    }
>   }
>   
>   /* No-op implementation of "begin_group_cb" for JSON output.  */
> @@ -268,6 +288,9 @@ diagnostic_output_format_init (diagnostic_context *context,
>   	context->end_group_cb =  json_end_group;
>   	context->final_cb =  json_final_cb;
>   
> +	/* The metadata is handled in JSON format, rather than as text.  */
> +	context->show_metadata = false;
> +
>   	/* The option is handled in JSON format, rather than as text.  */
>   	context->show_option_requested = false;
>   
> diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h
> new file mode 100644
> index 0000000..a759d44
> --- /dev/null
> +++ b/gcc/diagnostic-metadata.h
> @@ -0,0 +1,42 @@
> +/* Additional metadata for a diagnostic.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_DIAGNOSTIC_METADATA_H
> +#define GCC_DIAGNOSTIC_METADATA_H
> +
> +/* A bundle of additional metadata that can be associated with a
> +   diagnostic.
> +
> +   Currently this only supports associating a CWE identifier with a
> +   diagnostic.  */
> +
> +class diagnostic_metadata
> +{
> + public:
> +  diagnostic_metadata () : m_cwe (0) {}
> +
> +  void add_cwe (int cwe) { m_cwe = cwe; }
> +  int get_cwe () const { return m_cwe; }
> +
> + private:
> +  int m_cwe;
> +};
> +
> +#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
> diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
> index 81e0a10..e8d0613 100644
> --- a/gcc/diagnostic.c
> +++ b/gcc/diagnostic.c
> @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic.h"
>   #include "diagnostic-color.h"
>   #include "diagnostic-url.h"
> +#include "diagnostic-metadata.h"
>   #include "edit-context.h"
>   #include "selftest.h"
>   #include "selftest-diagnostic.h"
> @@ -58,11 +59,13 @@ along with GCC; see the file COPYING3.  If not see
>   #define permissive_error_option(DC) ((DC)->opt_permissive)
>   
>   /* Prototypes.  */
> -static bool diagnostic_impl (rich_location *, int, const char *,
> -			     va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(3,0);
> -static bool diagnostic_n_impl (rich_location *, int, unsigned HOST_WIDE_INT,
> +static bool diagnostic_impl (rich_location *, const diagnostic_metadata *,
> +			     int, const char *,
> +			     va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(4,0);
> +static bool diagnostic_n_impl (rich_location *, const diagnostic_metadata *,
> +			       int, unsigned HOST_WIDE_INT,
>   			       const char *, const char *, va_list *,
> -			       diagnostic_t) ATTRIBUTE_GCC_DIAG(5,0);
> +			       diagnostic_t) ATTRIBUTE_GCC_DIAG(6,0);
>   
>   static void error_recursion (diagnostic_context *) ATTRIBUTE_NORETURN;
>   static void real_abort (void) ATTRIBUTE_NORETURN;
> @@ -183,6 +186,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
>     diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
>     for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
>       context->caret_chars[i] = '^';
> +  context->show_metadata = false;
>     context->show_option_requested = false;
>     context->abort_on_error = false;
>     context->show_column = false;
> @@ -300,6 +304,7 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
>     diagnostic->message.format_spec = msg;
>     diagnostic->message.m_richloc = richloc;
>     diagnostic->richloc = richloc;
> +  diagnostic->metadata = NULL;
>     diagnostic->kind = kind;
>     diagnostic->option_index = 0;
>   }
> @@ -899,6 +904,47 @@ update_effective_level_from_pragmas (diagnostic_context *context,
>     return diag_class;
>   }
>   
> +/* Generate a URL string describing CWE.  The caller is responsible for
> +   freeing the string.  */
> +
> +static char *
> +get_cwe_url (int cwe)
> +{
> +  return xasprintf ("https://cwe.mitre.org/data/definitions/%i.html", cwe);
> +}
> +
> +/* If DIAGNOSTIC has metadata, print it.
> +
> +   For example, if the diagnostic metadata associates it with CWE-119,
> +   " [CWE-119]" will be printed, suitably colorized, and with a URL of a
> +   description of the security issue.  */
> +
> +static void
> +print_any_metadata (diagnostic_context *context,
> +		    const diagnostic_info *diagnostic)
> +{
> +  if (diagnostic->metadata == NULL)
> +    return;
> +
> +  int cwe = diagnostic->metadata->get_cwe ();
> +  if (cwe)
> +    {
> +      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 *cwe_url = get_cwe_url (cwe);
> +      pp_begin_url (pp, cwe_url);
> +      free (cwe_url);
> +      pp_printf (pp, "CWE-%i", cwe);
> +      pp_set_prefix (context->printer, saved_prefix);
> +      pp_end_url (pp);
> +      pp_string (pp, colorize_stop (pp_show_color (pp)));
> +      pp_character (pp, ']');
> +    }
> +}
> +
>   /* Print any metadata about the option used to control DIAGNOSTIC to CONTEXT's
>      printer, e.g. " [-Werror=uninitialized]".
>      Subroutine of diagnostic_report_diagnostic.  */
> @@ -1059,6 +1105,8 @@ diagnostic_report_diagnostic (diagnostic_context *context,
>     pp_format (context->printer, &diagnostic->message);
>     (*diagnostic_starter (context)) (context, diagnostic);
>     pp_output_formatted_text (context->printer);
> +  if (context->show_metadata)
> +    print_any_metadata (context, diagnostic);
>     if (context->show_option_requested)
>       print_option_information (context, diagnostic, orig_diag_kind);
>     (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);
> @@ -1190,8 +1238,8 @@ diagnostic_append_note (diagnostic_context *context,
>      permerror, error, error_at, error_at, sorry, fatal_error, internal_error,
>      and internal_error_no_backtrace, as documented and defined below.  */
>   static bool
> -diagnostic_impl (rich_location *richloc, int opt,
> -		 const char *gmsgid,
> +diagnostic_impl (rich_location *richloc, const diagnostic_metadata *metadata,
> +		 int opt, const char *gmsgid,
>   		 va_list *ap, diagnostic_t kind)
>   {
>     diagnostic_info diagnostic;
> @@ -1207,13 +1255,15 @@ diagnostic_impl (rich_location *richloc, int opt,
>         if (kind == DK_WARNING || kind == DK_PEDWARN)
>   	diagnostic.option_index = opt;
>       }
> +  diagnostic.metadata = metadata;
>     return diagnostic_report_diagnostic (global_dc, &diagnostic);
>   }
>   
>   /* Implement inform_n, warning_n, and error_n, as documented and
>      defined below.  */
>   static bool
> -diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
> +diagnostic_n_impl (rich_location *richloc, const diagnostic_metadata *metadata,
> +		   int opt, unsigned HOST_WIDE_INT n,
>   		   const char *singular_gmsgid,
>   		   const char *plural_gmsgid,
>   		   va_list *ap, diagnostic_t kind)
> @@ -1233,6 +1283,7 @@ diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
>     diagnostic_set_info_translated (&diagnostic, text, ap, richloc, kind);
>     if (kind == DK_WARNING)
>       diagnostic.option_index = opt;
> +  diagnostic.metadata = metadata;
>     return diagnostic_report_diagnostic (global_dc, &diagnostic);
>   }
>   
> @@ -1246,7 +1297,7 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, location);
> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, kind);
> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, kind);
>     va_end (ap);
>     return ret;
>   }
> @@ -1260,7 +1311,7 @@ emit_diagnostic (diagnostic_t kind, rich_location *richloc, int opt,
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, kind);
> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, kind);
>     va_end (ap);
>     return ret;
>   }
> @@ -1272,7 +1323,18 @@ emit_diagnostic_valist (diagnostic_t kind, location_t location, int opt,
>   			const char *gmsgid, va_list *ap)
>   {
>     rich_location richloc (line_table, location);
> -  return diagnostic_impl (&richloc, opt, gmsgid, ap, kind);
> +  return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind);
> +}
> +
> +/* Wrapper around diagnostic_impl taking a va_list parameter.  */
> +
> +bool
> +emit_diagnostic_valist (diagnostic_t kind, rich_location *richloc,
> +			const diagnostic_metadata *metadata,
> +			int opt,
> +			const char *gmsgid, va_list *ap)
> +{
> +  return diagnostic_impl (richloc, metadata, opt, gmsgid, ap, kind);
>   }
>   
>   /* An informative note at LOCATION.  Use this for additional details on an error
> @@ -1284,7 +1346,7 @@ inform (location_t location, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, location);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_NOTE);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
>     va_end (ap);
>   }
>   
> @@ -1297,7 +1359,7 @@ inform (rich_location *richloc, const char *gmsgid, ...)
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_NOTE);
> +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
>     va_end (ap);
>   }
>   
> @@ -1311,7 +1373,7 @@ inform_n (location_t location, unsigned HOST_WIDE_INT n,
>     va_start (ap, plural_gmsgid);
>     auto_diagnostic_group d;
>     rich_location richloc (line_table, location);
> -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid,
> +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid,
>   		     &ap, DK_NOTE);
>     va_end (ap);
>   }
> @@ -1326,7 +1388,7 @@ warning (int opt, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, input_location);
> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING);
> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
>     va_end (ap);
>     return ret;
>   }
> @@ -1342,7 +1404,7 @@ warning_at (location_t location, int opt, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, location);
> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING);
> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
>     va_end (ap);
>     return ret;
>   }
> @@ -1357,7 +1419,25 @@ warning_at (rich_location *richloc, int opt, const char *gmsgid, ...)
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_WARNING);
> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
> +  va_end (ap);
> +  return ret;
> +}
> +
> +/* Same as "warning at" above, but using METADATA.  */
> +
> +bool
> +warning_at (rich_location *richloc, const diagnostic_metadata &metadata,
> +	    int opt, const char *gmsgid, ...)
> +{
> +  gcc_assert (richloc);
> +
> +  auto_diagnostic_group d;
> +  va_list ap;
> +  va_start (ap, gmsgid);
> +  bool ret
> +    = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap,
> +		       DK_WARNING);
>     va_end (ap);
>     return ret;
>   }
> @@ -1373,7 +1453,7 @@ warning_n (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, plural_gmsgid);
> -  bool ret = diagnostic_n_impl (richloc, opt, n,
> +  bool ret = diagnostic_n_impl (richloc, NULL, opt, n,
>   				singular_gmsgid, plural_gmsgid,
>   				&ap, DK_WARNING);
>     va_end (ap);
> @@ -1392,7 +1472,7 @@ warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
>     va_list ap;
>     va_start (ap, plural_gmsgid);
>     rich_location richloc (line_table, location);
> -  bool ret = diagnostic_n_impl (&richloc, opt, n,
> +  bool ret = diagnostic_n_impl (&richloc, NULL, opt, n,
>   				singular_gmsgid, plural_gmsgid,
>   				&ap, DK_WARNING);
>     va_end (ap);
> @@ -1419,7 +1499,7 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, location);
> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_PEDWARN);
> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN);
>     va_end (ap);
>     return ret;
>   }
> @@ -1434,7 +1514,7 @@ pedwarn (rich_location *richloc, int opt, const char *gmsgid, ...)
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_PEDWARN);
> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN);
>     va_end (ap);
>     return ret;
>   }
> @@ -1453,7 +1533,7 @@ permerror (location_t location, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, location);
> -  bool ret = diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_PERMERROR);
> +  bool ret = diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR);
>     va_end (ap);
>     return ret;
>   }
> @@ -1468,7 +1548,7 @@ permerror (rich_location *richloc, const char *gmsgid, ...)
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  bool ret = diagnostic_impl (richloc, -1, gmsgid, &ap, DK_PERMERROR);
> +  bool ret = diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR);
>     va_end (ap);
>     return ret;
>   }
> @@ -1482,7 +1562,7 @@ error (const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, input_location);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>     va_end (ap);
>   }
>   
> @@ -1496,7 +1576,7 @@ error_n (location_t location, unsigned HOST_WIDE_INT n,
>     va_list ap;
>     va_start (ap, plural_gmsgid);
>     rich_location richloc (line_table, location);
> -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid,
> +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid,
>   		     &ap, DK_ERROR);
>     va_end (ap);
>   }
> @@ -1509,7 +1589,7 @@ error_at (location_t loc, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, loc);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>     va_end (ap);
>   }
>   
> @@ -1523,7 +1603,7 @@ error_at (rich_location *richloc, const char *gmsgid, ...)
>     auto_diagnostic_group d;
>     va_list ap;
>     va_start (ap, gmsgid);
> -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_ERROR);
> +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>     va_end (ap);
>   }
>   
> @@ -1537,7 +1617,7 @@ sorry (const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, input_location);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
>     va_end (ap);
>   }
>   
> @@ -1549,7 +1629,7 @@ sorry_at (location_t loc, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, loc);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
>     va_end (ap);
>   }
>   
> @@ -1571,7 +1651,7 @@ fatal_error (location_t loc, const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, loc);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_FATAL);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_FATAL);
>     va_end (ap);
>   
>     gcc_unreachable ();
> @@ -1588,7 +1668,7 @@ internal_error (const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, input_location);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE);
>     va_end (ap);
>   
>     gcc_unreachable ();
> @@ -1604,7 +1684,7 @@ internal_error_no_backtrace (const char *gmsgid, ...)
>     va_list ap;
>     va_start (ap, gmsgid);
>     rich_location richloc (line_table, input_location);
> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE_NOBT);
> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE_NOBT);
>     va_end (ap);
>   
>     gcc_unreachable ();
> diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
> index 2fb75e6..0e4b6ad 100644
> --- a/gcc/diagnostic.h
> +++ b/gcc/diagnostic.h
> @@ -46,6 +46,10 @@ struct diagnostic_info
>     /* The location at which the diagnostic is to be reported.  */
>     rich_location *richloc;
>   
> +  /* An optional bundle of metadata associated with the diagnostic
> +     (or NULL).  */
> +  const diagnostic_metadata *metadata;
> +
>     /* Auxiliary data for client.  */
>     void *x_data;
>     /* The kind of diagnostic it is about.  */
> @@ -126,6 +130,9 @@ struct diagnostic_context
>     /* Character used for caret diagnostics.  */
>     char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
>   
> +  /* True if we should print any metadata associated with diagnostics.  */
> +  bool show_metadata;
> +
>     /* True if we should print the command line option which controls
>        each diagnostic, if known.  */
>     bool show_option_requested;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index fc92913..209e2c6 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -279,6 +279,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
>   -fdiagnostics-nn-line-numbers @gol
> +-fno-diagnostics-show-metadata  @gol
>   -fdiagnostics-minimum-margin-width=@var{width} @gol
>   -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
>   -fdiagnostics-show-template-tree  -fno-elide-type @gol
> @@ -4012,6 +4013,13 @@ as the types of expressions:
>   This option suppresses the printing of these labels (in the example above,
>   the vertical bars and the ``char *'' and ``long int'' text).
>   
> +@item -fno-diagnostics-show-metadata
> +@opindex fno-diagnostics-show-metadata
> +@opindex fdiagnostics-show-metadata
> +Diagnostics can optionally contain @url{https://cwe.mitre.org/index.html, CWE}
> +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-line-numbers
>   @opindex fno-diagnostics-show-line-numbers
>   @opindex fdiagnostics-show-line-numbers
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 9928d01..0caaf04 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -2491,6 +2491,10 @@ common_handle_option (struct gcc_options *opts,
>         dc->parseable_fixits_p = value;
>         break;
>   
> +    case OPT_fdiagnostics_show_metadata:
> +      dc->show_metadata = value;
> +      break;
> +
>       case OPT_fdiagnostics_show_option:
>         dc->show_option_requested = value;
>         break;
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 2d0a390..7136164 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1102,6 +1102,8 @@ general_init (const char *argv0, bool init_signals)
>       = global_options_init.x_flag_diagnostics_show_line_numbers;
>     global_dc->use_nn_for_line_numbers_p
>       = global_options_init.x_flag_diagnostics_nn_line_numbers;
> +  global_dc->show_metadata
> +    = global_options_init.x_flag_diagnostics_show_metadata;
>     global_dc->show_option_requested
>       = global_options_init.x_flag_diagnostics_show_option;
>     global_dc->min_margin_width
>
David Malcolm Dec. 13, 2019, 2:59 a.m. UTC | #2
On Wed, 2019-12-04 at 10:36 -0700, Martin Sebor wrote:
> On 11/15/19 6:22 PM, David Malcolm wrote:
> > This patch adds support for associating a diagnostic with an
> > optional
> > diagnostic_metadata object, so that plugins can add extra data to
> > their
> > diagnostics (e.g. mapping a diagnostic to a taxonomy or coding
> > standard
> > such as from CERT or MISRA).
> > 
> > Currently this only supports associating a CWE identifier with a
> > diagnostic (which is what I'm using for the analyzer warnings later
> > in
> > the patch kit), but adding a diagnostic_metadata class allows for
> > future
> > growth in this area without an explosion of further "warning_at"
> > overloads
> > for all of the different kinds of custom data that a plugin might
> > want to
> > add.
> 
> We discussed this in the past so I'll be repeating some of my
> comments from that thread.  I like the feature at a high level,
> but I'm not sure I see how to make it work in a robust way.
> 
> There is no one-to-one mapping between GCC warnings and any of
> these classifications.  A single GCC warning might map to several
> CERT or MISRA guidelines, and a single guideline might map to two
> or more different GCC warnings.  This is an M to N mapping times
> the number of coding standards/classifications.  I haven't looked
> at the patch kit in enough detail to understand how it tries to
> handle it.  Can you explain?

The patch kit gives the ability to (optionally) specify a single CWE
identifier when emitting a diagnostic.

The patch kit uses this in various places for its new warnings for the
cases where there is a well-defined CWE weakness identifier (and
doesn't otherwise).

It doesn't attempt to classify any existing warnings.

It doesn't attempt to support other classifications in this patch, but
it does introduce a diagnostic_metadata object that could gain this
functionality at some later time, if we come up with a scheme we're
happy with (a slight violation of YAGNI, but it's such a pain touching
all the diagnostic fns that I prefer to do it once).

> My other concern here is that these guidelines evolve.  New ones
> are being added, existing ones moved or broken up, etc.  Does
> the infrastructure cope with this evolution (e.g., by reading
> the mapping from a file)?

Let's cross that bridge when we come to it.  If it becomes a problem,
then we can come up with a fix (e.g. a command-line option to specify
the CWE version).

Dave

> Martin
> 
> > gcc/ChangeLog:
> > 	* common.opt (fdiagnostics-show-metadata): New option.
> > 	* diagnostic-core.h (class diagnostic_metadata): New forward
> > decl.
> > 	(warning_at): Add overload taking a const diagnostic_metadata
> > &.
> > 	(emit_diagnostic_valist): Add overload taking a
> > 	const diagnostic_metadata *.
> > 	* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
> > 	(json_from_metadata): New function.
> > 	(json_end_diagnostic): Call it to add "metadata" child for
> > 	diagnostics with metadata.
> > 	(diagnostic_output_format_init): Clear context->show_metadata.
> > 	* diagnostic-metadata.h: New file.
> > 	* diagnostic.c: Include "diagnostic-metadata.h".
> > 	(diagnostic_impl): Add const diagnostic_metadata * param.
> > 	(diagnostic_n_impl): Likewise.
> > 	(diagnostic_initialize): Initialize context->show_metadata.
> > 	(diagnostic_set_info_translated): Initialize diagnostic-
> > >metadata.
> > 	(get_cwe_url): New function.
> > 	(print_any_metadata): New function.
> > 	(diagnostic_report_diagnostic): Call print_any_metadata if the
> > 	diagnostic has non-NULL metadata.
> > 	(emit_diagnostic): Pass NULL as the metadata in the call to
> > 	diagnostic_impl.
> > 	(emit_diagnostic_valist): Likewise.
> > 	(emit_diagnostic_valist): New overload taking a
> > 	const diagnostic_metadata *.
> > 	(inform): Pass NULL as the metadata in the call to
> > 	diagnostic_impl.
> > 	(inform_n): Likewise for diagnostic_n_impl.
> > 	(warning): Likewise.
> > 	(warning_at): Likewise.  Add overload that takes a
> > 	const diagnostic_metadata &.
> > 	(warning_n): Pass NULL as the metadata in the call to
> > 	diagnostic_n_impl.
> > 	(pedwarn): Likewise for diagnostic_impl.
> > 	(permerror): Likewise.
> > 	(error): Likewise.
> > 	(error_n): Likewise.
> > 	(error_at): Likewise.
> > 	(sorry): Likewise.
> > 	(sorry_at): Likewise.
> > 	(fatal_error): Likewise.
> > 	(internal_error): Likewise.
> > 	(internal_error_no_backtrace): Likewise.
> > 	* diagnostic.h (diagnostic_info::metadata): New field.
> > 	(diagnostic_context::show_metadata): New field.
> > 	* doc/invoke.texi (-fno-diagnostics-show-metadata): New option.
> > 	* opts.c (common_handle_option): Handle
> > 	OPT_fdiagnostics_show_metadata.
> > 	* toplev.c (general_init): Initialize global_dc->show_metadata.
> > ---
> >   gcc/common.opt                |   4 ++
> >   gcc/diagnostic-core.h         |  10 +++
> >   gcc/diagnostic-format-json.cc |  23 +++++++
> >   gcc/diagnostic-metadata.h     |  42 +++++++++++++
> >   gcc/diagnostic.c              | 142
> > +++++++++++++++++++++++++++++++++---------
> >   gcc/diagnostic.h              |   7 +++
> >   gcc/doc/invoke.texi           |   8 +++
> >   gcc/opts.c                    |   4 ++
> >   gcc/toplev.c                  |   2 +
> >   9 files changed, 211 insertions(+), 31 deletions(-)
> >   create mode 100644 gcc/diagnostic-metadata.h
> > 
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 3c024b3..228df32 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -1334,6 +1334,10 @@ fdiagnostics-show-option
> >   Common Var(flag_diagnostics_show_option) Init(1)
> >   Amend appropriate diagnostic messages with the command line
> > option that controls them.
> >   
> > +fdiagnostics-show-metadata
> > +Common Var(flag_diagnostics_show_metadata) Init(1)
> > +Print additional metadata for diagnostic messages, where
> > available.
> > +
> >   fdiagnostics-minimum-margin-width=
> >   Common Joined UInteger Var(diagnostics_minimum_margin_width)
> > Init(6)
> >   Set minimum width of left margin of source code when showing
> > source.
> > diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
> > index efafde4..2e7f120 100644
> > --- a/gcc/diagnostic-core.h
> > +++ b/gcc/diagnostic-core.h
> > @@ -45,6 +45,9 @@ class auto_diagnostic_group
> >     ~auto_diagnostic_group ();
> >   };
> >   
> > +/* Forward decl.  */
> > +class diagnostic_metadata; /* See diagnostic-metadata.h.  */
> > +
> >   extern const char *progname;
> >   
> >   extern const char *trim_filename (const char *);
> > @@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const
> > char *, ...)
> >       ATTRIBUTE_GCC_DIAG(3,4);
> >   extern bool warning_at (rich_location *, int, const char *, ...)
> >       ATTRIBUTE_GCC_DIAG(3,4);
> > +extern bool warning_at (rich_location *, const diagnostic_metadata
> > &, int,
> > +			const char *, ...)
> > +    ATTRIBUTE_GCC_DIAG(4,5);
> >   extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
> >   extern void error_n (location_t, unsigned HOST_WIDE_INT, const
> > char *,
> >   		     const char *, ...)
> > @@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t,
> > rich_location *, int,
> >   			     const char *, ...)
> > ATTRIBUTE_GCC_DIAG(4,5);
> >   extern bool emit_diagnostic_valist (diagnostic_t, location_t,
> > int, const char *,
> >   				    va_list *) ATTRIBUTE_GCC_DIAG
> > (4,0);
> > +extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
> > +				    const diagnostic_metadata
> > *metadata,
> > +				    int, const char *, va_list *)
> > +  ATTRIBUTE_GCC_DIAG (5,0);
> >   extern bool seen_error (void);
> >   
> >   #ifdef BUFSIZ
> > diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-
> > json.cc
> > index eb99952..200e844 100644
> > --- a/gcc/diagnostic-format-json.cc
> > +++ b/gcc/diagnostic-format-json.cc
> > @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >   #include "system.h"
> >   #include "coretypes.h"
> >   #include "diagnostic.h"
> > +#include "diagnostic-metadata.h"
> >   #include "json.h"
> >   #include "selftest.h"
> >   
> > @@ -103,6 +104,19 @@ json_from_fixit_hint (const fixit_hint *hint)
> >     return fixit_obj;
> >   }
> >   
> > +/* Generate a JSON object for METADATA.  */
> > +
> > +static json::object *
> > +json_from_metadata (const diagnostic_metadata *metadata)
> > +{
> > +  json::object *metadata_obj = new json::object ();
> > +
> > +  if (metadata->get_cwe ())
> > +    metadata_obj->set ("cwe", new json::number (metadata->get_cwe
> > ()));
> > +
> > +  return metadata_obj;
> > +}
> > +
> >   /* No-op implementation of "begin_diagnostic" for JSON
> > output.  */
> >   
> >   static void
> > @@ -211,6 +225,12 @@ json_end_diagnostic (diagnostic_context
> > *context, diagnostic_info *diagnostic,
> >        TODO: functions
> >        TODO: inlining information
> >        TODO: macro expansion information.  */
> > +
> > +  if (diagnostic->metadata)
> > +    {
> > +      json::object *metadata_obj = json_from_metadata (diagnostic-
> > >metadata);
> > +      diag_obj->set ("metadata", metadata_obj);
> > +    }
> >   }
> >   
> >   /* No-op implementation of "begin_group_cb" for JSON output.  */
> > @@ -268,6 +288,9 @@ diagnostic_output_format_init
> > (diagnostic_context *context,
> >   	context->end_group_cb =  json_end_group;
> >   	context->final_cb =  json_final_cb;
> >   
> > +	/* The metadata is handled in JSON format, rather than as
> > text.  */
> > +	context->show_metadata = false;
> > +
> >   	/* The option is handled in JSON format, rather than as
> > text.  */
> >   	context->show_option_requested = false;
> >   
> > diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h
> > new file mode 100644
> > index 0000000..a759d44
> > --- /dev/null
> > +++ b/gcc/diagnostic-metadata.h
> > @@ -0,0 +1,42 @@
> > +/* Additional metadata for a diagnostic.
> > +   Copyright (C) 2019 Free Software Foundation, Inc.
> > +   Contributed by David Malcolm <dmalcolm@redhat.com>
> > +
> > +This file is part of GCC.
> > +
> > +GCC is free software; you can redistribute it and/or modify it
> > under
> > +the terms of the GNU General Public License as published by the
> > Free
> > +Software Foundation; either version 3, or (at your option) any
> > later
> > +version.
> > +
> > +GCC is distributed in the hope that it will be useful, but WITHOUT
> > ANY
> > +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > License
> > +for more details.
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3.  If not see
> > +<http://www.gnu.org/licenses/>;.  */
> > +
> > +#ifndef GCC_DIAGNOSTIC_METADATA_H
> > +#define GCC_DIAGNOSTIC_METADATA_H
> > +
> > +/* A bundle of additional metadata that can be associated with a
> > +   diagnostic.
> > +
> > +   Currently this only supports associating a CWE identifier with
> > a
> > +   diagnostic.  */
> > +
> > +class diagnostic_metadata
> > +{
> > + public:
> > +  diagnostic_metadata () : m_cwe (0) {}
> > +
> > +  void add_cwe (int cwe) { m_cwe = cwe; }
> > +  int get_cwe () const { return m_cwe; }
> > +
> > + private:
> > +  int m_cwe;
> > +};
> > +
> > +#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
> > diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
> > index 81e0a10..e8d0613 100644
> > --- a/gcc/diagnostic.c
> > +++ b/gcc/diagnostic.c
> > @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >   #include "diagnostic.h"
> >   #include "diagnostic-color.h"
> >   #include "diagnostic-url.h"
> > +#include "diagnostic-metadata.h"
> >   #include "edit-context.h"
> >   #include "selftest.h"
> >   #include "selftest-diagnostic.h"
> > @@ -58,11 +59,13 @@ along with GCC; see the file COPYING3.  If not
> > see
> >   #define permissive_error_option(DC) ((DC)->opt_permissive)
> >   
> >   /* Prototypes.  */
> > -static bool diagnostic_impl (rich_location *, int, const char *,
> > -			     va_list *, diagnostic_t)
> > ATTRIBUTE_GCC_DIAG(3,0);
> > -static bool diagnostic_n_impl (rich_location *, int, unsigned
> > HOST_WIDE_INT,
> > +static bool diagnostic_impl (rich_location *, const
> > diagnostic_metadata *,
> > +			     int, const char *,
> > +			     va_list *, diagnostic_t)
> > ATTRIBUTE_GCC_DIAG(4,0);
> > +static bool diagnostic_n_impl (rich_location *, const
> > diagnostic_metadata *,
> > +			       int, unsigned HOST_WIDE_INT,
> >   			       const char *, const char *, va_list *,
> > -			       diagnostic_t) ATTRIBUTE_GCC_DIAG(5,0);
> > +			       diagnostic_t) ATTRIBUTE_GCC_DIAG(6,0);
> >   
> >   static void error_recursion (diagnostic_context *)
> > ATTRIBUTE_NORETURN;
> >   static void real_abort (void) ATTRIBUTE_NORETURN;
> > @@ -183,6 +186,7 @@ diagnostic_initialize (diagnostic_context
> > *context, int n_opts)
> >     diagnostic_set_caret_max_width (context, pp_line_cutoff
> > (context->printer));
> >     for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES;
> > i++)
> >       context->caret_chars[i] = '^';
> > +  context->show_metadata = false;
> >     context->show_option_requested = false;
> >     context->abort_on_error = false;
> >     context->show_column = false;
> > @@ -300,6 +304,7 @@ diagnostic_set_info_translated (diagnostic_info
> > *diagnostic, const char *msg,
> >     diagnostic->message.format_spec = msg;
> >     diagnostic->message.m_richloc = richloc;
> >     diagnostic->richloc = richloc;
> > +  diagnostic->metadata = NULL;
> >     diagnostic->kind = kind;
> >     diagnostic->option_index = 0;
> >   }
> > @@ -899,6 +904,47 @@ update_effective_level_from_pragmas
> > (diagnostic_context *context,
> >     return diag_class;
> >   }
> >   
> > +/* Generate a URL string describing CWE.  The caller is
> > responsible for
> > +   freeing the string.  */
> > +
> > +static char *
> > +get_cwe_url (int cwe)
> > +{
> > +  return xasprintf ("
> > https://cwe.mitre.org/data/definitions/%i.html", cwe);
> > +}
> > +
> > +/* If DIAGNOSTIC has metadata, print it.
> > +
> > +   For example, if the diagnostic metadata associates it with CWE-
> > 119,
> > +   " [CWE-119]" will be printed, suitably colorized, and with a
> > URL of a
> > +   description of the security issue.  */
> > +
> > +static void
> > +print_any_metadata (diagnostic_context *context,
> > +		    const diagnostic_info *diagnostic)
> > +{
> > +  if (diagnostic->metadata == NULL)
> > +    return;
> > +
> > +  int cwe = diagnostic->metadata->get_cwe ();
> > +  if (cwe)
> > +    {
> > +      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 *cwe_url = get_cwe_url (cwe);
> > +      pp_begin_url (pp, cwe_url);
> > +      free (cwe_url);
> > +      pp_printf (pp, "CWE-%i", cwe);
> > +      pp_set_prefix (context->printer, saved_prefix);
> > +      pp_end_url (pp);
> > +      pp_string (pp, colorize_stop (pp_show_color (pp)));
> > +      pp_character (pp, ']');
> > +    }
> > +}
> > +
> >   /* Print any metadata about the option used to control DIAGNOSTIC
> > to CONTEXT's
> >      printer, e.g. " [-Werror=uninitialized]".
> >      Subroutine of diagnostic_report_diagnostic.  */
> > @@ -1059,6 +1105,8 @@ diagnostic_report_diagnostic
> > (diagnostic_context *context,
> >     pp_format (context->printer, &diagnostic->message);
> >     (*diagnostic_starter (context)) (context, diagnostic);
> >     pp_output_formatted_text (context->printer);
> > +  if (context->show_metadata)
> > +    print_any_metadata (context, diagnostic);
> >     if (context->show_option_requested)
> >       print_option_information (context, diagnostic,
> > orig_diag_kind);
> >     (*diagnostic_finalizer (context)) (context, diagnostic,
> > orig_diag_kind);
> > @@ -1190,8 +1238,8 @@ diagnostic_append_note (diagnostic_context
> > *context,
> >      permerror, error, error_at, error_at, sorry, fatal_error,
> > internal_error,
> >      and internal_error_no_backtrace, as documented and defined
> > below.  */
> >   static bool
> > -diagnostic_impl (rich_location *richloc, int opt,
> > -		 const char *gmsgid,
> > +diagnostic_impl (rich_location *richloc, const diagnostic_metadata
> > *metadata,
> > +		 int opt, const char *gmsgid,
> >   		 va_list *ap, diagnostic_t kind)
> >   {
> >     diagnostic_info diagnostic;
> > @@ -1207,13 +1255,15 @@ diagnostic_impl (rich_location *richloc,
> > int opt,
> >         if (kind == DK_WARNING || kind == DK_PEDWARN)
> >   	diagnostic.option_index = opt;
> >       }
> > +  diagnostic.metadata = metadata;
> >     return diagnostic_report_diagnostic (global_dc, &diagnostic);
> >   }
> >   
> >   /* Implement inform_n, warning_n, and error_n, as documented and
> >      defined below.  */
> >   static bool
> > -diagnostic_n_impl (rich_location *richloc, int opt, unsigned
> > HOST_WIDE_INT n,
> > +diagnostic_n_impl (rich_location *richloc, const
> > diagnostic_metadata *metadata,
> > +		   int opt, unsigned HOST_WIDE_INT n,
> >   		   const char *singular_gmsgid,
> >   		   const char *plural_gmsgid,
> >   		   va_list *ap, diagnostic_t kind)
> > @@ -1233,6 +1283,7 @@ diagnostic_n_impl (rich_location *richloc,
> > int opt, unsigned HOST_WIDE_INT n,
> >     diagnostic_set_info_translated (&diagnostic, text, ap, richloc,
> > kind);
> >     if (kind == DK_WARNING)
> >       diagnostic.option_index = opt;
> > +  diagnostic.metadata = metadata;
> >     return diagnostic_report_diagnostic (global_dc, &diagnostic);
> >   }
> >   
> > @@ -1246,7 +1297,7 @@ emit_diagnostic (diagnostic_t kind,
> > location_t location, int opt,
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, location);
> > -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, kind);
> > +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
> > kind);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1260,7 +1311,7 @@ emit_diagnostic (diagnostic_t kind,
> > rich_location *richloc, int opt,
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, kind);
> > +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
> > kind);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1272,7 +1323,18 @@ emit_diagnostic_valist (diagnostic_t kind,
> > location_t location, int opt,
> >   			const char *gmsgid, va_list *ap)
> >   {
> >     rich_location richloc (line_table, location);
> > -  return diagnostic_impl (&richloc, opt, gmsgid, ap, kind);
> > +  return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind);
> > +}
> > +
> > +/* Wrapper around diagnostic_impl taking a va_list parameter.  */
> > +
> > +bool
> > +emit_diagnostic_valist (diagnostic_t kind, rich_location *richloc,
> > +			const diagnostic_metadata *metadata,
> > +			int opt,
> > +			const char *gmsgid, va_list *ap)
> > +{
> > +  return diagnostic_impl (richloc, metadata, opt, gmsgid, ap,
> > kind);
> >   }
> >   
> >   /* An informative note at LOCATION.  Use this for additional
> > details on an error
> > @@ -1284,7 +1346,7 @@ inform (location_t location, const char
> > *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, location);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_NOTE);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
> >     va_end (ap);
> >   }
> >   
> > @@ -1297,7 +1359,7 @@ inform (rich_location *richloc, const char
> > *gmsgid, ...)
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_NOTE);
> > +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
> >     va_end (ap);
> >   }
> >   
> > @@ -1311,7 +1373,7 @@ inform_n (location_t location, unsigned
> > HOST_WIDE_INT n,
> >     va_start (ap, plural_gmsgid);
> >     auto_diagnostic_group d;
> >     rich_location richloc (line_table, location);
> > -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid,
> > plural_gmsgid,
> > +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid,
> > plural_gmsgid,
> >   		     &ap, DK_NOTE);
> >     va_end (ap);
> >   }
> > @@ -1326,7 +1388,7 @@ warning (int opt, const char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, input_location);
> > -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
> > DK_WARNING);
> > +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
> > DK_WARNING);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1342,7 +1404,7 @@ warning_at (location_t location, int opt,
> > const char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, location);
> > -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
> > DK_WARNING);
> > +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
> > DK_WARNING);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1357,7 +1419,25 @@ warning_at (rich_location *richloc, int opt,
> > const char *gmsgid, ...)
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap,
> > DK_WARNING);
> > +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
> > DK_WARNING);
> > +  va_end (ap);
> > +  return ret;
> > +}
> > +
> > +/* Same as "warning at" above, but using METADATA.  */
> > +
> > +bool
> > +warning_at (rich_location *richloc, const diagnostic_metadata
> > &metadata,
> > +	    int opt, const char *gmsgid, ...)
> > +{
> > +  gcc_assert (richloc);
> > +
> > +  auto_diagnostic_group d;
> > +  va_list ap;
> > +  va_start (ap, gmsgid);
> > +  bool ret
> > +    = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap,
> > +		       DK_WARNING);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1373,7 +1453,7 @@ warning_n (rich_location *richloc, int opt,
> > unsigned HOST_WIDE_INT n,
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, plural_gmsgid);
> > -  bool ret = diagnostic_n_impl (richloc, opt, n,
> > +  bool ret = diagnostic_n_impl (richloc, NULL, opt, n,
> >   				singular_gmsgid, plural_gmsgid,
> >   				&ap, DK_WARNING);
> >     va_end (ap);
> > @@ -1392,7 +1472,7 @@ warning_n (location_t location, int opt,
> > unsigned HOST_WIDE_INT n,
> >     va_list ap;
> >     va_start (ap, plural_gmsgid);
> >     rich_location richloc (line_table, location);
> > -  bool ret = diagnostic_n_impl (&richloc, opt, n,
> > +  bool ret = diagnostic_n_impl (&richloc, NULL, opt, n,
> >   				singular_gmsgid, plural_gmsgid,
> >   				&ap, DK_WARNING);
> >     va_end (ap);
> > @@ -1419,7 +1499,7 @@ pedwarn (location_t location, int opt, const
> > char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, location);
> > -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
> > DK_PEDWARN);
> > +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
> > DK_PEDWARN);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1434,7 +1514,7 @@ pedwarn (rich_location *richloc, int opt,
> > const char *gmsgid, ...)
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap,
> > DK_PEDWARN);
> > +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
> > DK_PEDWARN);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1453,7 +1533,7 @@ permerror (location_t location, const char
> > *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, location);
> > -  bool ret = diagnostic_impl (&richloc, -1, gmsgid, &ap,
> > DK_PERMERROR);
> > +  bool ret = diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap,
> > DK_PERMERROR);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1468,7 +1548,7 @@ permerror (rich_location *richloc, const char
> > *gmsgid, ...)
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  bool ret = diagnostic_impl (richloc, -1, gmsgid, &ap,
> > DK_PERMERROR);
> > +  bool ret = diagnostic_impl (richloc, NULL, -1, gmsgid, &ap,
> > DK_PERMERROR);
> >     va_end (ap);
> >     return ret;
> >   }
> > @@ -1482,7 +1562,7 @@ error (const char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, input_location);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
> >     va_end (ap);
> >   }
> >   
> > @@ -1496,7 +1576,7 @@ error_n (location_t location, unsigned
> > HOST_WIDE_INT n,
> >     va_list ap;
> >     va_start (ap, plural_gmsgid);
> >     rich_location richloc (line_table, location);
> > -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid,
> > plural_gmsgid,
> > +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid,
> > plural_gmsgid,
> >   		     &ap, DK_ERROR);
> >     va_end (ap);
> >   }
> > @@ -1509,7 +1589,7 @@ error_at (location_t loc, const char *gmsgid,
> > ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, loc);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
> >     va_end (ap);
> >   }
> >   
> > @@ -1523,7 +1603,7 @@ error_at (rich_location *richloc, const char
> > *gmsgid, ...)
> >     auto_diagnostic_group d;
> >     va_list ap;
> >     va_start (ap, gmsgid);
> > -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_ERROR);
> > +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
> >     va_end (ap);
> >   }
> >   
> > @@ -1537,7 +1617,7 @@ sorry (const char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, input_location);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
> >     va_end (ap);
> >   }
> >   
> > @@ -1549,7 +1629,7 @@ sorry_at (location_t loc, const char *gmsgid,
> > ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, loc);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
> >     va_end (ap);
> >   }
> >   
> > @@ -1571,7 +1651,7 @@ fatal_error (location_t loc, const char
> > *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, loc);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_FATAL);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_FATAL);
> >     va_end (ap);
> >   
> >     gcc_unreachable ();
> > @@ -1588,7 +1668,7 @@ internal_error (const char *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, input_location);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE);
> >     va_end (ap);
> >   
> >     gcc_unreachable ();
> > @@ -1604,7 +1684,7 @@ internal_error_no_backtrace (const char
> > *gmsgid, ...)
> >     va_list ap;
> >     va_start (ap, gmsgid);
> >     rich_location richloc (line_table, input_location);
> > -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE_NOBT);
> > +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE_NOBT);
> >     va_end (ap);
> >   
> >     gcc_unreachable ();
> > diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
> > index 2fb75e6..0e4b6ad 100644
> > --- a/gcc/diagnostic.h
> > +++ b/gcc/diagnostic.h
> > @@ -46,6 +46,10 @@ struct diagnostic_info
> >     /* The location at which the diagnostic is to be reported.  */
> >     rich_location *richloc;
> >   
> > +  /* An optional bundle of metadata associated with the diagnostic
> > +     (or NULL).  */
> > +  const diagnostic_metadata *metadata;
> > +
> >     /* Auxiliary data for client.  */
> >     void *x_data;
> >     /* The kind of diagnostic it is about.  */
> > @@ -126,6 +130,9 @@ struct diagnostic_context
> >     /* Character used for caret diagnostics.  */
> >     char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
> >   
> > +  /* True if we should print any metadata associated with
> > diagnostics.  */
> > +  bool show_metadata;
> > +
> >     /* True if we should print the command line option which
> > controls
> >        each diagnostic, if known.  */
> >     bool show_option_requested;
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index fc92913..209e2c6 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -279,6 +279,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
> >   -fdiagnostics-nn-line-numbers @gol
> > +-fno-diagnostics-show-metadata  @gol
> >   -fdiagnostics-minimum-margin-width=@var{width} @gol
> >   -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
> >   -fdiagnostics-show-template-tree  -fno-elide-type @gol
> > @@ -4012,6 +4013,13 @@ as the types of expressions:
> >   This option suppresses the printing of these labels (in the
> > example above,
> >   the vertical bars and the ``char *'' and ``long int'' text).
> >   
> > +@item -fno-diagnostics-show-metadata
> > +@opindex fno-diagnostics-show-metadata
> > +@opindex fdiagnostics-show-metadata
> > +Diagnostics can optionally contain @url{
> > https://cwe.mitre.org/index.html, CWE}
> > +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-line-numbers
> >   @opindex fno-diagnostics-show-line-numbers
> >   @opindex fdiagnostics-show-line-numbers
> > diff --git a/gcc/opts.c b/gcc/opts.c
> > index 9928d01..0caaf04 100644
> > --- a/gcc/opts.c
> > +++ b/gcc/opts.c
> > @@ -2491,6 +2491,10 @@ common_handle_option (struct gcc_options
> > *opts,
> >         dc->parseable_fixits_p = value;
> >         break;
> >   
> > +    case OPT_fdiagnostics_show_metadata:
> > +      dc->show_metadata = value;
> > +      break;
> > +
> >       case OPT_fdiagnostics_show_option:
> >         dc->show_option_requested = value;
> >         break;
> > diff --git a/gcc/toplev.c b/gcc/toplev.c
> > index 2d0a390..7136164 100644
> > --- a/gcc/toplev.c
> > +++ b/gcc/toplev.c
> > @@ -1102,6 +1102,8 @@ general_init (const char *argv0, bool
> > init_signals)
> >       = global_options_init.x_flag_diagnostics_show_line_numbers;
> >     global_dc->use_nn_for_line_numbers_p
> >       = global_options_init.x_flag_diagnostics_nn_line_numbers;
> > +  global_dc->show_metadata
> > +    = global_options_init.x_flag_diagnostics_show_metadata;
> >     global_dc->show_option_requested
> >       = global_options_init.x_flag_diagnostics_show_option;
> >     global_dc->min_margin_width
> >
Martin Sebor Dec. 13, 2019, 4:43 p.m. UTC | #3
On 12/12/19 7:59 PM, David Malcolm wrote:
> On Wed, 2019-12-04 at 10:36 -0700, Martin Sebor wrote:
>> On 11/15/19 6:22 PM, David Malcolm wrote:
>>> This patch adds support for associating a diagnostic with an
>>> optional
>>> diagnostic_metadata object, so that plugins can add extra data to
>>> their
>>> diagnostics (e.g. mapping a diagnostic to a taxonomy or coding
>>> standard
>>> such as from CERT or MISRA).
>>>
>>> Currently this only supports associating a CWE identifier with a
>>> diagnostic (which is what I'm using for the analyzer warnings later
>>> in
>>> the patch kit), but adding a diagnostic_metadata class allows for
>>> future
>>> growth in this area without an explosion of further "warning_at"
>>> overloads
>>> for all of the different kinds of custom data that a plugin might
>>> want to
>>> add.
>>
>> We discussed this in the past so I'll be repeating some of my
>> comments from that thread.  I like the feature at a high level,
>> but I'm not sure I see how to make it work in a robust way.
>>
>> There is no one-to-one mapping between GCC warnings and any of
>> these classifications.  A single GCC warning might map to several
>> CERT or MISRA guidelines, and a single guideline might map to two
>> or more different GCC warnings.  This is an M to N mapping times
>> the number of coding standards/classifications.  I haven't looked
>> at the patch kit in enough detail to understand how it tries to
>> handle it.  Can you explain?
> 
> The patch kit gives the ability to (optionally) specify a single CWE
> identifier when emitting a diagnostic.
> 
> The patch kit uses this in various places for its new warnings for the
> cases where there is a well-defined CWE weakness identifier (and
> doesn't otherwise).
> 
> It doesn't attempt to classify any existing warnings.
> 
> It doesn't attempt to support other classifications in this patch, but
> it does introduce a diagnostic_metadata object that could gain this
> functionality at some later time, if we come up with a scheme we're
> happy with (a slight violation of YAGNI, but it's such a pain touching
> all the diagnostic fns that I prefer to do it once).

I wasn't necessarily asking about any classifications in particular,
or even about pre-existing warnings.  I think it would be nice to be
able to eventually make it work for them but I obviously realize that
will take quite a bit retrofitting.

What I'd like to understand how the warning X classification-id
mapping for the new analyzer warnings is supported in the design
of the (new) diagnostic infrastructure (or how you envision it
will be when it's more complete).

You said the analyzer currently only supports associating a CWE
identifier with a diagnostic.  By "diagnostic" do you mean
a particular instance of a given warning or all its instances?

Picking an uninitialized use as an example, there are three CWE's
that discuss it:

   CWE-457: Use of Uninitialized Variable
   CWE-824: Access of Uninitialized Pointer
   CWE-908: Use of Uninitialized Resource

but only one -Wanalyzer-use-of-uninitialized-value.  I'd like to
see how the mapping works (or will work) between these three CWE's
and instances of this analyzer option/warning.

I see on Goldbolt that for either the use of an uninitialized
pointer (CWE-457) or for its dereference (CWE-824) the analyzer
issues the same warning with the same CWE ID:

   use of uninitialized value 'p' [CWE-457] 
[-Wanalyzer-use-of-uninitialized-value]

It doesn't differentiate between the two CWE's.

If this limitation is baked into the design of the infrastructure
then that's an obvious flaw that needs to be fixed.  There is no
point in issuing warnings with the wrong CWE's.  Anyone who cares
about CWE's would view those as bugs, and those who don't care
don't need to see them.

>> My other concern here is that these guidelines evolve.  New ones
>> are being added, existing ones moved or broken up, etc.  Does
>> the infrastructure cope with this evolution (e.g., by reading
>> the mapping from a file)?
> 
> Let's cross that bridge when we come to it.  If it becomes a problem,
> then we can come up with a fix (e.g. a command-line option to specify
> the CWE version).
I agree that dealing with evolving classifications is a secondary
goal that can probably be fairly easily met.  I wanted to know if
there already was a solution for it now.

But referencing the right IDs in the diagnostics seems like a basic
design requirement.  Unless the above example is due to the mapping
not being fully populated yet it seems clear that issuing a warning
with one CWE ID for a bug described under another CWE ID is
a problem.

Martin

> 
> Dave
> 
>> Martin
>>
>>> gcc/ChangeLog:
>>> 	* common.opt (fdiagnostics-show-metadata): New option.
>>> 	* diagnostic-core.h (class diagnostic_metadata): New forward
>>> decl.
>>> 	(warning_at): Add overload taking a const diagnostic_metadata
>>> &.
>>> 	(emit_diagnostic_valist): Add overload taking a
>>> 	const diagnostic_metadata *.
>>> 	* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
>>> 	(json_from_metadata): New function.
>>> 	(json_end_diagnostic): Call it to add "metadata" child for
>>> 	diagnostics with metadata.
>>> 	(diagnostic_output_format_init): Clear context->show_metadata.
>>> 	* diagnostic-metadata.h: New file.
>>> 	* diagnostic.c: Include "diagnostic-metadata.h".
>>> 	(diagnostic_impl): Add const diagnostic_metadata * param.
>>> 	(diagnostic_n_impl): Likewise.
>>> 	(diagnostic_initialize): Initialize context->show_metadata.
>>> 	(diagnostic_set_info_translated): Initialize diagnostic-
>>>> metadata.
>>> 	(get_cwe_url): New function.
>>> 	(print_any_metadata): New function.
>>> 	(diagnostic_report_diagnostic): Call print_any_metadata if the
>>> 	diagnostic has non-NULL metadata.
>>> 	(emit_diagnostic): Pass NULL as the metadata in the call to
>>> 	diagnostic_impl.
>>> 	(emit_diagnostic_valist): Likewise.
>>> 	(emit_diagnostic_valist): New overload taking a
>>> 	const diagnostic_metadata *.
>>> 	(inform): Pass NULL as the metadata in the call to
>>> 	diagnostic_impl.
>>> 	(inform_n): Likewise for diagnostic_n_impl.
>>> 	(warning): Likewise.
>>> 	(warning_at): Likewise.  Add overload that takes a
>>> 	const diagnostic_metadata &.
>>> 	(warning_n): Pass NULL as the metadata in the call to
>>> 	diagnostic_n_impl.
>>> 	(pedwarn): Likewise for diagnostic_impl.
>>> 	(permerror): Likewise.
>>> 	(error): Likewise.
>>> 	(error_n): Likewise.
>>> 	(error_at): Likewise.
>>> 	(sorry): Likewise.
>>> 	(sorry_at): Likewise.
>>> 	(fatal_error): Likewise.
>>> 	(internal_error): Likewise.
>>> 	(internal_error_no_backtrace): Likewise.
>>> 	* diagnostic.h (diagnostic_info::metadata): New field.
>>> 	(diagnostic_context::show_metadata): New field.
>>> 	* doc/invoke.texi (-fno-diagnostics-show-metadata): New option.
>>> 	* opts.c (common_handle_option): Handle
>>> 	OPT_fdiagnostics_show_metadata.
>>> 	* toplev.c (general_init): Initialize global_dc->show_metadata.
>>> ---
>>>    gcc/common.opt                |   4 ++
>>>    gcc/diagnostic-core.h         |  10 +++
>>>    gcc/diagnostic-format-json.cc |  23 +++++++
>>>    gcc/diagnostic-metadata.h     |  42 +++++++++++++
>>>    gcc/diagnostic.c              | 142
>>> +++++++++++++++++++++++++++++++++---------
>>>    gcc/diagnostic.h              |   7 +++
>>>    gcc/doc/invoke.texi           |   8 +++
>>>    gcc/opts.c                    |   4 ++
>>>    gcc/toplev.c                  |   2 +
>>>    9 files changed, 211 insertions(+), 31 deletions(-)
>>>    create mode 100644 gcc/diagnostic-metadata.h
>>>
>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>> index 3c024b3..228df32 100644
>>> --- a/gcc/common.opt
>>> +++ b/gcc/common.opt
>>> @@ -1334,6 +1334,10 @@ fdiagnostics-show-option
>>>    Common Var(flag_diagnostics_show_option) Init(1)
>>>    Amend appropriate diagnostic messages with the command line
>>> option that controls them.
>>>    
>>> +fdiagnostics-show-metadata
>>> +Common Var(flag_diagnostics_show_metadata) Init(1)
>>> +Print additional metadata for diagnostic messages, where
>>> available.
>>> +
>>>    fdiagnostics-minimum-margin-width=
>>>    Common Joined UInteger Var(diagnostics_minimum_margin_width)
>>> Init(6)
>>>    Set minimum width of left margin of source code when showing
>>> source.
>>> diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
>>> index efafde4..2e7f120 100644
>>> --- a/gcc/diagnostic-core.h
>>> +++ b/gcc/diagnostic-core.h
>>> @@ -45,6 +45,9 @@ class auto_diagnostic_group
>>>      ~auto_diagnostic_group ();
>>>    };
>>>    
>>> +/* Forward decl.  */
>>> +class diagnostic_metadata; /* See diagnostic-metadata.h.  */
>>> +
>>>    extern const char *progname;
>>>    
>>>    extern const char *trim_filename (const char *);
>>> @@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const
>>> char *, ...)
>>>        ATTRIBUTE_GCC_DIAG(3,4);
>>>    extern bool warning_at (rich_location *, int, const char *, ...)
>>>        ATTRIBUTE_GCC_DIAG(3,4);
>>> +extern bool warning_at (rich_location *, const diagnostic_metadata
>>> &, int,
>>> +			const char *, ...)
>>> +    ATTRIBUTE_GCC_DIAG(4,5);
>>>    extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
>>>    extern void error_n (location_t, unsigned HOST_WIDE_INT, const
>>> char *,
>>>    		     const char *, ...)
>>> @@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t,
>>> rich_location *, int,
>>>    			     const char *, ...)
>>> ATTRIBUTE_GCC_DIAG(4,5);
>>>    extern bool emit_diagnostic_valist (diagnostic_t, location_t,
>>> int, const char *,
>>>    				    va_list *) ATTRIBUTE_GCC_DIAG
>>> (4,0);
>>> +extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
>>> +				    const diagnostic_metadata
>>> *metadata,
>>> +				    int, const char *, va_list *)
>>> +  ATTRIBUTE_GCC_DIAG (5,0);
>>>    extern bool seen_error (void);
>>>    
>>>    #ifdef BUFSIZ
>>> diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-
>>> json.cc
>>> index eb99952..200e844 100644
>>> --- a/gcc/diagnostic-format-json.cc
>>> +++ b/gcc/diagnostic-format-json.cc
>>> @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not
>>> see
>>>    #include "system.h"
>>>    #include "coretypes.h"
>>>    #include "diagnostic.h"
>>> +#include "diagnostic-metadata.h"
>>>    #include "json.h"
>>>    #include "selftest.h"
>>>    
>>> @@ -103,6 +104,19 @@ json_from_fixit_hint (const fixit_hint *hint)
>>>      return fixit_obj;
>>>    }
>>>    
>>> +/* Generate a JSON object for METADATA.  */
>>> +
>>> +static json::object *
>>> +json_from_metadata (const diagnostic_metadata *metadata)
>>> +{
>>> +  json::object *metadata_obj = new json::object ();
>>> +
>>> +  if (metadata->get_cwe ())
>>> +    metadata_obj->set ("cwe", new json::number (metadata->get_cwe
>>> ()));
>>> +
>>> +  return metadata_obj;
>>> +}
>>> +
>>>    /* No-op implementation of "begin_diagnostic" for JSON
>>> output.  */
>>>    
>>>    static void
>>> @@ -211,6 +225,12 @@ json_end_diagnostic (diagnostic_context
>>> *context, diagnostic_info *diagnostic,
>>>         TODO: functions
>>>         TODO: inlining information
>>>         TODO: macro expansion information.  */
>>> +
>>> +  if (diagnostic->metadata)
>>> +    {
>>> +      json::object *metadata_obj = json_from_metadata (diagnostic-
>>>> metadata);
>>> +      diag_obj->set ("metadata", metadata_obj);
>>> +    }
>>>    }
>>>    
>>>    /* No-op implementation of "begin_group_cb" for JSON output.  */
>>> @@ -268,6 +288,9 @@ diagnostic_output_format_init
>>> (diagnostic_context *context,
>>>    	context->end_group_cb =  json_end_group;
>>>    	context->final_cb =  json_final_cb;
>>>    
>>> +	/* The metadata is handled in JSON format, rather than as
>>> text.  */
>>> +	context->show_metadata = false;
>>> +
>>>    	/* The option is handled in JSON format, rather than as
>>> text.  */
>>>    	context->show_option_requested = false;
>>>    
>>> diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h
>>> new file mode 100644
>>> index 0000000..a759d44
>>> --- /dev/null
>>> +++ b/gcc/diagnostic-metadata.h
>>> @@ -0,0 +1,42 @@
>>> +/* Additional metadata for a diagnostic.
>>> +   Copyright (C) 2019 Free Software Foundation, Inc.
>>> +   Contributed by David Malcolm <dmalcolm@redhat.com>
>>> +
>>> +This file is part of GCC.
>>> +
>>> +GCC is free software; you can redistribute it and/or modify it
>>> under
>>> +the terms of the GNU General Public License as published by the
>>> Free
>>> +Software Foundation; either version 3, or (at your option) any
>>> later
>>> +version.
>>> +
>>> +GCC is distributed in the hope that it will be useful, but WITHOUT
>>> ANY
>>> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
>>> License
>>> +for more details.
>>> +
>>> +You should have received a copy of the GNU General Public License
>>> +along with GCC; see the file COPYING3.  If not see
>>> +<http://www.gnu.org/licenses/>;.  */
>>> +
>>> +#ifndef GCC_DIAGNOSTIC_METADATA_H
>>> +#define GCC_DIAGNOSTIC_METADATA_H
>>> +
>>> +/* A bundle of additional metadata that can be associated with a
>>> +   diagnostic.
>>> +
>>> +   Currently this only supports associating a CWE identifier with
>>> a
>>> +   diagnostic.  */
>>> +
>>> +class diagnostic_metadata
>>> +{
>>> + public:
>>> +  diagnostic_metadata () : m_cwe (0) {}
>>> +
>>> +  void add_cwe (int cwe) { m_cwe = cwe; }
>>> +  int get_cwe () const { return m_cwe; }
>>> +
>>> + private:
>>> +  int m_cwe;
>>> +};
>>> +
>>> +#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
>>> diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
>>> index 81e0a10..e8d0613 100644
>>> --- a/gcc/diagnostic.c
>>> +++ b/gcc/diagnostic.c
>>> @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not
>>> see
>>>    #include "diagnostic.h"
>>>    #include "diagnostic-color.h"
>>>    #include "diagnostic-url.h"
>>> +#include "diagnostic-metadata.h"
>>>    #include "edit-context.h"
>>>    #include "selftest.h"
>>>    #include "selftest-diagnostic.h"
>>> @@ -58,11 +59,13 @@ along with GCC; see the file COPYING3.  If not
>>> see
>>>    #define permissive_error_option(DC) ((DC)->opt_permissive)
>>>    
>>>    /* Prototypes.  */
>>> -static bool diagnostic_impl (rich_location *, int, const char *,
>>> -			     va_list *, diagnostic_t)
>>> ATTRIBUTE_GCC_DIAG(3,0);
>>> -static bool diagnostic_n_impl (rich_location *, int, unsigned
>>> HOST_WIDE_INT,
>>> +static bool diagnostic_impl (rich_location *, const
>>> diagnostic_metadata *,
>>> +			     int, const char *,
>>> +			     va_list *, diagnostic_t)
>>> ATTRIBUTE_GCC_DIAG(4,0);
>>> +static bool diagnostic_n_impl (rich_location *, const
>>> diagnostic_metadata *,
>>> +			       int, unsigned HOST_WIDE_INT,
>>>    			       const char *, const char *, va_list *,
>>> -			       diagnostic_t) ATTRIBUTE_GCC_DIAG(5,0);
>>> +			       diagnostic_t) ATTRIBUTE_GCC_DIAG(6,0);
>>>    
>>>    static void error_recursion (diagnostic_context *)
>>> ATTRIBUTE_NORETURN;
>>>    static void real_abort (void) ATTRIBUTE_NORETURN;
>>> @@ -183,6 +186,7 @@ diagnostic_initialize (diagnostic_context
>>> *context, int n_opts)
>>>      diagnostic_set_caret_max_width (context, pp_line_cutoff
>>> (context->printer));
>>>      for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES;
>>> i++)
>>>        context->caret_chars[i] = '^';
>>> +  context->show_metadata = false;
>>>      context->show_option_requested = false;
>>>      context->abort_on_error = false;
>>>      context->show_column = false;
>>> @@ -300,6 +304,7 @@ diagnostic_set_info_translated (diagnostic_info
>>> *diagnostic, const char *msg,
>>>      diagnostic->message.format_spec = msg;
>>>      diagnostic->message.m_richloc = richloc;
>>>      diagnostic->richloc = richloc;
>>> +  diagnostic->metadata = NULL;
>>>      diagnostic->kind = kind;
>>>      diagnostic->option_index = 0;
>>>    }
>>> @@ -899,6 +904,47 @@ update_effective_level_from_pragmas
>>> (diagnostic_context *context,
>>>      return diag_class;
>>>    }
>>>    
>>> +/* Generate a URL string describing CWE.  The caller is
>>> responsible for
>>> +   freeing the string.  */
>>> +
>>> +static char *
>>> +get_cwe_url (int cwe)
>>> +{
>>> +  return xasprintf ("
>>> https://cwe.mitre.org/data/definitions/%i.html", cwe);
>>> +}
>>> +
>>> +/* If DIAGNOSTIC has metadata, print it.
>>> +
>>> +   For example, if the diagnostic metadata associates it with CWE-
>>> 119,
>>> +   " [CWE-119]" will be printed, suitably colorized, and with a
>>> URL of a
>>> +   description of the security issue.  */
>>> +
>>> +static void
>>> +print_any_metadata (diagnostic_context *context,
>>> +		    const diagnostic_info *diagnostic)
>>> +{
>>> +  if (diagnostic->metadata == NULL)
>>> +    return;
>>> +
>>> +  int cwe = diagnostic->metadata->get_cwe ();
>>> +  if (cwe)
>>> +    {
>>> +      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 *cwe_url = get_cwe_url (cwe);
>>> +      pp_begin_url (pp, cwe_url);
>>> +      free (cwe_url);
>>> +      pp_printf (pp, "CWE-%i", cwe);
>>> +      pp_set_prefix (context->printer, saved_prefix);
>>> +      pp_end_url (pp);
>>> +      pp_string (pp, colorize_stop (pp_show_color (pp)));
>>> +      pp_character (pp, ']');
>>> +    }
>>> +}
>>> +
>>>    /* Print any metadata about the option used to control DIAGNOSTIC
>>> to CONTEXT's
>>>       printer, e.g. " [-Werror=uninitialized]".
>>>       Subroutine of diagnostic_report_diagnostic.  */
>>> @@ -1059,6 +1105,8 @@ diagnostic_report_diagnostic
>>> (diagnostic_context *context,
>>>      pp_format (context->printer, &diagnostic->message);
>>>      (*diagnostic_starter (context)) (context, diagnostic);
>>>      pp_output_formatted_text (context->printer);
>>> +  if (context->show_metadata)
>>> +    print_any_metadata (context, diagnostic);
>>>      if (context->show_option_requested)
>>>        print_option_information (context, diagnostic,
>>> orig_diag_kind);
>>>      (*diagnostic_finalizer (context)) (context, diagnostic,
>>> orig_diag_kind);
>>> @@ -1190,8 +1238,8 @@ diagnostic_append_note (diagnostic_context
>>> *context,
>>>       permerror, error, error_at, error_at, sorry, fatal_error,
>>> internal_error,
>>>       and internal_error_no_backtrace, as documented and defined
>>> below.  */
>>>    static bool
>>> -diagnostic_impl (rich_location *richloc, int opt,
>>> -		 const char *gmsgid,
>>> +diagnostic_impl (rich_location *richloc, const diagnostic_metadata
>>> *metadata,
>>> +		 int opt, const char *gmsgid,
>>>    		 va_list *ap, diagnostic_t kind)
>>>    {
>>>      diagnostic_info diagnostic;
>>> @@ -1207,13 +1255,15 @@ diagnostic_impl (rich_location *richloc,
>>> int opt,
>>>          if (kind == DK_WARNING || kind == DK_PEDWARN)
>>>    	diagnostic.option_index = opt;
>>>        }
>>> +  diagnostic.metadata = metadata;
>>>      return diagnostic_report_diagnostic (global_dc, &diagnostic);
>>>    }
>>>    
>>>    /* Implement inform_n, warning_n, and error_n, as documented and
>>>       defined below.  */
>>>    static bool
>>> -diagnostic_n_impl (rich_location *richloc, int opt, unsigned
>>> HOST_WIDE_INT n,
>>> +diagnostic_n_impl (rich_location *richloc, const
>>> diagnostic_metadata *metadata,
>>> +		   int opt, unsigned HOST_WIDE_INT n,
>>>    		   const char *singular_gmsgid,
>>>    		   const char *plural_gmsgid,
>>>    		   va_list *ap, diagnostic_t kind)
>>> @@ -1233,6 +1283,7 @@ diagnostic_n_impl (rich_location *richloc,
>>> int opt, unsigned HOST_WIDE_INT n,
>>>      diagnostic_set_info_translated (&diagnostic, text, ap, richloc,
>>> kind);
>>>      if (kind == DK_WARNING)
>>>        diagnostic.option_index = opt;
>>> +  diagnostic.metadata = metadata;
>>>      return diagnostic_report_diagnostic (global_dc, &diagnostic);
>>>    }
>>>    
>>> @@ -1246,7 +1297,7 @@ emit_diagnostic (diagnostic_t kind,
>>> location_t location, int opt,
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, kind);
>>> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
>>> kind);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1260,7 +1311,7 @@ emit_diagnostic (diagnostic_t kind,
>>> rich_location *richloc, int opt,
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, kind);
>>> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
>>> kind);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1272,7 +1323,18 @@ emit_diagnostic_valist (diagnostic_t kind,
>>> location_t location, int opt,
>>>    			const char *gmsgid, va_list *ap)
>>>    {
>>>      rich_location richloc (line_table, location);
>>> -  return diagnostic_impl (&richloc, opt, gmsgid, ap, kind);
>>> +  return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind);
>>> +}
>>> +
>>> +/* Wrapper around diagnostic_impl taking a va_list parameter.  */
>>> +
>>> +bool
>>> +emit_diagnostic_valist (diagnostic_t kind, rich_location *richloc,
>>> +			const diagnostic_metadata *metadata,
>>> +			int opt,
>>> +			const char *gmsgid, va_list *ap)
>>> +{
>>> +  return diagnostic_impl (richloc, metadata, opt, gmsgid, ap,
>>> kind);
>>>    }
>>>    
>>>    /* An informative note at LOCATION.  Use this for additional
>>> details on an error
>>> @@ -1284,7 +1346,7 @@ inform (location_t location, const char
>>> *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_NOTE);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1297,7 +1359,7 @@ inform (rich_location *richloc, const char
>>> *gmsgid, ...)
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_NOTE);
>>> +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1311,7 +1373,7 @@ inform_n (location_t location, unsigned
>>> HOST_WIDE_INT n,
>>>      va_start (ap, plural_gmsgid);
>>>      auto_diagnostic_group d;
>>>      rich_location richloc (line_table, location);
>>> -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid,
>>> plural_gmsgid,
>>> +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid,
>>> plural_gmsgid,
>>>    		     &ap, DK_NOTE);
>>>      va_end (ap);
>>>    }
>>> @@ -1326,7 +1388,7 @@ warning (int opt, const char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, input_location);
>>> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
>>> DK_WARNING);
>>> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
>>> DK_WARNING);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1342,7 +1404,7 @@ warning_at (location_t location, int opt,
>>> const char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
>>> DK_WARNING);
>>> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
>>> DK_WARNING);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1357,7 +1419,25 @@ warning_at (rich_location *richloc, int opt,
>>> const char *gmsgid, ...)
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap,
>>> DK_WARNING);
>>> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
>>> DK_WARNING);
>>> +  va_end (ap);
>>> +  return ret;
>>> +}
>>> +
>>> +/* Same as "warning at" above, but using METADATA.  */
>>> +
>>> +bool
>>> +warning_at (rich_location *richloc, const diagnostic_metadata
>>> &metadata,
>>> +	    int opt, const char *gmsgid, ...)
>>> +{
>>> +  gcc_assert (richloc);
>>> +
>>> +  auto_diagnostic_group d;
>>> +  va_list ap;
>>> +  va_start (ap, gmsgid);
>>> +  bool ret
>>> +    = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap,
>>> +		       DK_WARNING);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1373,7 +1453,7 @@ warning_n (rich_location *richloc, int opt,
>>> unsigned HOST_WIDE_INT n,
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, plural_gmsgid);
>>> -  bool ret = diagnostic_n_impl (richloc, opt, n,
>>> +  bool ret = diagnostic_n_impl (richloc, NULL, opt, n,
>>>    				singular_gmsgid, plural_gmsgid,
>>>    				&ap, DK_WARNING);
>>>      va_end (ap);
>>> @@ -1392,7 +1472,7 @@ warning_n (location_t location, int opt,
>>> unsigned HOST_WIDE_INT n,
>>>      va_list ap;
>>>      va_start (ap, plural_gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  bool ret = diagnostic_n_impl (&richloc, opt, n,
>>> +  bool ret = diagnostic_n_impl (&richloc, NULL, opt, n,
>>>    				singular_gmsgid, plural_gmsgid,
>>>    				&ap, DK_WARNING);
>>>      va_end (ap);
>>> @@ -1419,7 +1499,7 @@ pedwarn (location_t location, int opt, const
>>> char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap,
>>> DK_PEDWARN);
>>> +  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap,
>>> DK_PEDWARN);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1434,7 +1514,7 @@ pedwarn (rich_location *richloc, int opt,
>>> const char *gmsgid, ...)
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap,
>>> DK_PEDWARN);
>>> +  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap,
>>> DK_PEDWARN);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1453,7 +1533,7 @@ permerror (location_t location, const char
>>> *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  bool ret = diagnostic_impl (&richloc, -1, gmsgid, &ap,
>>> DK_PERMERROR);
>>> +  bool ret = diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap,
>>> DK_PERMERROR);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1468,7 +1548,7 @@ permerror (rich_location *richloc, const char
>>> *gmsgid, ...)
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  bool ret = diagnostic_impl (richloc, -1, gmsgid, &ap,
>>> DK_PERMERROR);
>>> +  bool ret = diagnostic_impl (richloc, NULL, -1, gmsgid, &ap,
>>> DK_PERMERROR);
>>>      va_end (ap);
>>>      return ret;
>>>    }
>>> @@ -1482,7 +1562,7 @@ error (const char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, input_location);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1496,7 +1576,7 @@ error_n (location_t location, unsigned
>>> HOST_WIDE_INT n,
>>>      va_list ap;
>>>      va_start (ap, plural_gmsgid);
>>>      rich_location richloc (line_table, location);
>>> -  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid,
>>> plural_gmsgid,
>>> +  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid,
>>> plural_gmsgid,
>>>    		     &ap, DK_ERROR);
>>>      va_end (ap);
>>>    }
>>> @@ -1509,7 +1589,7 @@ error_at (location_t loc, const char *gmsgid,
>>> ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, loc);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1523,7 +1603,7 @@ error_at (rich_location *richloc, const char
>>> *gmsgid, ...)
>>>      auto_diagnostic_group d;
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>> -  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_ERROR);
>>> +  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1537,7 +1617,7 @@ sorry (const char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, input_location);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1549,7 +1629,7 @@ sorry_at (location_t loc, const char *gmsgid,
>>> ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, loc);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
>>>      va_end (ap);
>>>    }
>>>    
>>> @@ -1571,7 +1651,7 @@ fatal_error (location_t loc, const char
>>> *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, loc);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_FATAL);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_FATAL);
>>>      va_end (ap);
>>>    
>>>      gcc_unreachable ();
>>> @@ -1588,7 +1668,7 @@ internal_error (const char *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, input_location);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE);
>>>      va_end (ap);
>>>    
>>>      gcc_unreachable ();
>>> @@ -1604,7 +1684,7 @@ internal_error_no_backtrace (const char
>>> *gmsgid, ...)
>>>      va_list ap;
>>>      va_start (ap, gmsgid);
>>>      rich_location richloc (line_table, input_location);
>>> -  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE_NOBT);
>>> +  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE_NOBT);
>>>      va_end (ap);
>>>    
>>>      gcc_unreachable ();
>>> diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
>>> index 2fb75e6..0e4b6ad 100644
>>> --- a/gcc/diagnostic.h
>>> +++ b/gcc/diagnostic.h
>>> @@ -46,6 +46,10 @@ struct diagnostic_info
>>>      /* The location at which the diagnostic is to be reported.  */
>>>      rich_location *richloc;
>>>    
>>> +  /* An optional bundle of metadata associated with the diagnostic
>>> +     (or NULL).  */
>>> +  const diagnostic_metadata *metadata;
>>> +
>>>      /* Auxiliary data for client.  */
>>>      void *x_data;
>>>      /* The kind of diagnostic it is about.  */
>>> @@ -126,6 +130,9 @@ struct diagnostic_context
>>>      /* Character used for caret diagnostics.  */
>>>      char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
>>>    
>>> +  /* True if we should print any metadata associated with
>>> diagnostics.  */
>>> +  bool show_metadata;
>>> +
>>>      /* True if we should print the command line option which
>>> controls
>>>         each diagnostic, if known.  */
>>>      bool show_option_requested;
>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>> index fc92913..209e2c6 100644
>>> --- a/gcc/doc/invoke.texi
>>> +++ b/gcc/doc/invoke.texi
>>> @@ -279,6 +279,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
>>>    -fdiagnostics-nn-line-numbers @gol
>>> +-fno-diagnostics-show-metadata  @gol
>>>    -fdiagnostics-minimum-margin-width=@var{width} @gol
>>>    -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
>>>    -fdiagnostics-show-template-tree  -fno-elide-type @gol
>>> @@ -4012,6 +4013,13 @@ as the types of expressions:
>>>    This option suppresses the printing of these labels (in the
>>> example above,
>>>    the vertical bars and the ``char *'' and ``long int'' text).
>>>    
>>> +@item -fno-diagnostics-show-metadata
>>> +@opindex fno-diagnostics-show-metadata
>>> +@opindex fdiagnostics-show-metadata
>>> +Diagnostics can optionally contain @url{
>>> https://cwe.mitre.org/index.html, CWE}
>>> +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-line-numbers
>>>    @opindex fno-diagnostics-show-line-numbers
>>>    @opindex fdiagnostics-show-line-numbers
>>> diff --git a/gcc/opts.c b/gcc/opts.c
>>> index 9928d01..0caaf04 100644
>>> --- a/gcc/opts.c
>>> +++ b/gcc/opts.c
>>> @@ -2491,6 +2491,10 @@ common_handle_option (struct gcc_options
>>> *opts,
>>>          dc->parseable_fixits_p = value;
>>>          break;
>>>    
>>> +    case OPT_fdiagnostics_show_metadata:
>>> +      dc->show_metadata = value;
>>> +      break;
>>> +
>>>        case OPT_fdiagnostics_show_option:
>>>          dc->show_option_requested = value;
>>>          break;
>>> diff --git a/gcc/toplev.c b/gcc/toplev.c
>>> index 2d0a390..7136164 100644
>>> --- a/gcc/toplev.c
>>> +++ b/gcc/toplev.c
>>> @@ -1102,6 +1102,8 @@ general_init (const char *argv0, bool
>>> init_signals)
>>>        = global_options_init.x_flag_diagnostics_show_line_numbers;
>>>      global_dc->use_nn_for_line_numbers_p
>>>        = global_options_init.x_flag_diagnostics_nn_line_numbers;
>>> +  global_dc->show_metadata
>>> +    = global_options_init.x_flag_diagnostics_show_metadata;
>>>      global_dc->show_option_requested
>>>        = global_options_init.x_flag_diagnostics_show_option;
>>>      global_dc->min_margin_width
>>>
>
David Malcolm Dec. 13, 2019, 5:32 p.m. UTC | #4
On Fri, 2019-12-13 at 09:43 -0700, Martin Sebor wrote:
> On 12/12/19 7:59 PM, David Malcolm wrote:
> > On Wed, 2019-12-04 at 10:36 -0700, Martin Sebor wrote:
> > > On 11/15/19 6:22 PM, David Malcolm wrote:
> > > > This patch adds support for associating a diagnostic with an
> > > > optional
> > > > diagnostic_metadata object, so that plugins can add extra data
> > > > to
> > > > their
> > > > diagnostics (e.g. mapping a diagnostic to a taxonomy or coding
> > > > standard
> > > > such as from CERT or MISRA).
> > > > 
> > > > Currently this only supports associating a CWE identifier with
> > > > a
> > > > diagnostic (which is what I'm using for the analyzer warnings
> > > > later
> > > > in
> > > > the patch kit), but adding a diagnostic_metadata class allows
> > > > for
> > > > future
> > > > growth in this area without an explosion of further
> > > > "warning_at"
> > > > overloads
> > > > for all of the different kinds of custom data that a plugin
> > > > might
> > > > want to
> > > > add.
> > > 
> > > We discussed this in the past so I'll be repeating some of my
> > > comments from that thread.  I like the feature at a high level,
> > > but I'm not sure I see how to make it work in a robust way.
> > > 
> > > There is no one-to-one mapping between GCC warnings and any of
> > > these classifications.  A single GCC warning might map to several
> > > CERT or MISRA guidelines, and a single guideline might map to two
> > > or more different GCC warnings.  This is an M to N mapping times
> > > the number of coding standards/classifications.  I haven't looked
> > > at the patch kit in enough detail to understand how it tries to
> > > handle it.  Can you explain?
> > 
> > The patch kit gives the ability to (optionally) specify a single
> > CWE
> > identifier when emitting a diagnostic.
> > 
> > The patch kit uses this in various places for its new warnings for
> > the
> > cases where there is a well-defined CWE weakness identifier (and
> > doesn't otherwise).
> > 
> > It doesn't attempt to classify any existing warnings.
> > 
> > It doesn't attempt to support other classifications in this patch,
> > but
> > it does introduce a diagnostic_metadata object that could gain this
> > functionality at some later time, if we come up with a scheme we're
> > happy with (a slight violation of YAGNI, but it's such a pain
> > touching
> > all the diagnostic fns that I prefer to do it once).
> 
> I wasn't necessarily asking about any classifications in particular,
> or even about pre-existing warnings.  I think it would be nice to be
> able to eventually make it work for them but I obviously realize that
> will take quite a bit retrofitting.
> 
> What I'd like to understand how the warning X classification-id
> mapping for the new analyzer warnings is supported in the design
> of the (new) diagnostic infrastructure (or how you envision it
> will be when it's more complete).
> 
> You said the analyzer currently only supports associating a CWE
> identifier with a diagnostic.  By "diagnostic" do you mean
> a particular instance of a given warning or all its instances?
> 
> Picking an uninitialized use as an example, there are three CWE's
> that discuss it:
> 
>    CWE-457: Use of Uninitialized Variable
>    CWE-824: Access of Uninitialized Pointer
>    CWE-908: Use of Uninitialized Resource
> 
> but only one -Wanalyzer-use-of-uninitialized-value.  I'd like to
> see how the mapping works (or will work) between these three CWE's
> and instances of this analyzer option/warning.
> 
> I see on Goldbolt that for either the use of an uninitialized
> pointer (CWE-457) or for its dereference (CWE-824) the analyzer
> issues the same warning with the same CWE ID:
> 
>    use of uninitialized value 'p' [CWE-457] 
> [-Wanalyzer-use-of-uninitialized-value]
> 
> It doesn't differentiate between the two CWE's.

That warning is implemented in analyzer/region-model.cc (see:
  https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01554.html )

The code in question is in poisoned_value_diagnostic::emit

      case POISON_KIND_UNINIT:
	{
	  diagnostic_metadata m;
	  m.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable".  */
	  return warning_at (rich_loc, m,
			     OPT_Wanalyzer_use_of_uninitialized_value,
			     "use of uninitialized value %qE",
			     m_expr);
	}
	break;

and currently that's code is only called when detecting a dereference
of an uninitialized pointer.

So, yes, this one probably should be CWE-824, but that's merely a bug
in my code, not a problem with the "infrastructure".

It looks like I'll need an extra attribute for that class to
distinguish the type of access (read from local seems to be CWE-457,
deref pointer seems to be CWE-824; CWE-908 would be emitted from a
different place, if I'm understanding the definition correctly).


> If this limitation is baked into the design of the infrastructure
> then that's an obvious flaw that needs to be fixed.  There is no
> point in issuing warnings with the wrong CWE's.  Anyone who cares
> about CWE's would view those as bugs, and those who don't care
> don't need to see them.
> 
> > > My other concern here is that these guidelines evolve.  New ones
> > > are being added, existing ones moved or broken up, etc.  Does
> > > the infrastructure cope with this evolution (e.g., by reading
> > > the mapping from a file)?
> > 
> > Let's cross that bridge when we come to it.  If it becomes a
> > problem,
> > then we can come up with a fix (e.g. a command-line option to
> > specify
> > the CWE version).
> I agree that dealing with evolving classifications is a secondary
> goal that can probably be fairly easily met.  I wanted to know if
> there already was a solution for it now.
> 
> But referencing the right IDs in the diagnostics seems like a basic
> design requirement.  

If you find that I'm using the wrong IDs, then tell me, and I'll fix
them.

> Unless the above example is due to the mapping
> not being fully populated yet it seems clear that issuing a warning
> with one CWE ID for a bug described under another CWE ID is
> a problem.

I'm not sure what you mean by a "mapping" being "populated".  It's
effectively just an optional int value that we can supply at diagnostic
calls.  It's different from the option ID, so yes, we can support
different CWEs for the same option ID if need be.  There isn't support
for multiple CWEs at the same instance of a warning (that could be
added if need be, but I wasn't planning to).

Dave
diff mbox series

Patch

diff --git a/gcc/common.opt b/gcc/common.opt
index 3c024b3..228df32 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1334,6 +1334,10 @@  fdiagnostics-show-option
 Common Var(flag_diagnostics_show_option) Init(1)
 Amend appropriate diagnostic messages with the command line option that controls them.
 
+fdiagnostics-show-metadata
+Common Var(flag_diagnostics_show_metadata) Init(1)
+Print additional metadata for diagnostic messages, where available.
+
 fdiagnostics-minimum-margin-width=
 Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
 Set minimum width of left margin of source code when showing source.
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index efafde4..2e7f120 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -45,6 +45,9 @@  class auto_diagnostic_group
   ~auto_diagnostic_group ();
 };
 
+/* Forward decl.  */
+class diagnostic_metadata; /* See diagnostic-metadata.h.  */
+
 extern const char *progname;
 
 extern const char *trim_filename (const char *);
@@ -78,6 +81,9 @@  extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at (rich_location *, const diagnostic_metadata &, int,
+			const char *, ...)
+    ATTRIBUTE_GCC_DIAG(4,5);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
@@ -109,6 +115,10 @@  extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
 			     const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
 extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
 				    va_list *) ATTRIBUTE_GCC_DIAG (4,0);
+extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
+				    const diagnostic_metadata *metadata,
+				    int, const char *, va_list *)
+  ATTRIBUTE_GCC_DIAG (5,0);
 extern bool seen_error (void);
 
 #ifdef BUFSIZ
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index eb99952..200e844 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "diagnostic.h"
+#include "diagnostic-metadata.h"
 #include "json.h"
 #include "selftest.h"
 
@@ -103,6 +104,19 @@  json_from_fixit_hint (const fixit_hint *hint)
   return fixit_obj;
 }
 
+/* Generate a JSON object for METADATA.  */
+
+static json::object *
+json_from_metadata (const diagnostic_metadata *metadata)
+{
+  json::object *metadata_obj = new json::object ();
+
+  if (metadata->get_cwe ())
+    metadata_obj->set ("cwe", new json::number (metadata->get_cwe ()));
+
+  return metadata_obj;
+}
+
 /* No-op implementation of "begin_diagnostic" for JSON output.  */
 
 static void
@@ -211,6 +225,12 @@  json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
      TODO: functions
      TODO: inlining information
      TODO: macro expansion information.  */
+
+  if (diagnostic->metadata)
+    {
+      json::object *metadata_obj = json_from_metadata (diagnostic->metadata);
+      diag_obj->set ("metadata", metadata_obj);
+    }
 }
 
 /* No-op implementation of "begin_group_cb" for JSON output.  */
@@ -268,6 +288,9 @@  diagnostic_output_format_init (diagnostic_context *context,
 	context->end_group_cb =  json_end_group;
 	context->final_cb =  json_final_cb;
 
+	/* The metadata is handled in JSON format, rather than as text.  */
+	context->show_metadata = false;
+
 	/* The option is handled in JSON format, rather than as text.  */
 	context->show_option_requested = false;
 
diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h
new file mode 100644
index 0000000..a759d44
--- /dev/null
+++ b/gcc/diagnostic-metadata.h
@@ -0,0 +1,42 @@ 
+/* Additional metadata for a diagnostic.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_DIAGNOSTIC_METADATA_H
+#define GCC_DIAGNOSTIC_METADATA_H
+
+/* A bundle of additional metadata that can be associated with a
+   diagnostic.
+
+   Currently this only supports associating a CWE identifier with a
+   diagnostic.  */
+
+class diagnostic_metadata
+{
+ public:
+  diagnostic_metadata () : m_cwe (0) {}
+
+  void add_cwe (int cwe) { m_cwe = cwe; }
+  int get_cwe () const { return m_cwe; }
+
+ private:
+  int m_cwe;
+};
+
+#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 81e0a10..e8d0613 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -32,6 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "diagnostic-color.h"
 #include "diagnostic-url.h"
+#include "diagnostic-metadata.h"
 #include "edit-context.h"
 #include "selftest.h"
 #include "selftest-diagnostic.h"
@@ -58,11 +59,13 @@  along with GCC; see the file COPYING3.  If not see
 #define permissive_error_option(DC) ((DC)->opt_permissive)
 
 /* Prototypes.  */
-static bool diagnostic_impl (rich_location *, int, const char *,
-			     va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(3,0);
-static bool diagnostic_n_impl (rich_location *, int, unsigned HOST_WIDE_INT,
+static bool diagnostic_impl (rich_location *, const diagnostic_metadata *,
+			     int, const char *,
+			     va_list *, diagnostic_t) ATTRIBUTE_GCC_DIAG(4,0);
+static bool diagnostic_n_impl (rich_location *, const diagnostic_metadata *,
+			       int, unsigned HOST_WIDE_INT,
 			       const char *, const char *, va_list *,
-			       diagnostic_t) ATTRIBUTE_GCC_DIAG(5,0);
+			       diagnostic_t) ATTRIBUTE_GCC_DIAG(6,0);
 
 static void error_recursion (diagnostic_context *) ATTRIBUTE_NORETURN;
 static void real_abort (void) ATTRIBUTE_NORETURN;
@@ -183,6 +186,7 @@  diagnostic_initialize (diagnostic_context *context, int n_opts)
   diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
   for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
     context->caret_chars[i] = '^';
+  context->show_metadata = false;
   context->show_option_requested = false;
   context->abort_on_error = false;
   context->show_column = false;
@@ -300,6 +304,7 @@  diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
   diagnostic->message.format_spec = msg;
   diagnostic->message.m_richloc = richloc;
   diagnostic->richloc = richloc;
+  diagnostic->metadata = NULL;
   diagnostic->kind = kind;
   diagnostic->option_index = 0;
 }
@@ -899,6 +904,47 @@  update_effective_level_from_pragmas (diagnostic_context *context,
   return diag_class;
 }
 
+/* Generate a URL string describing CWE.  The caller is responsible for
+   freeing the string.  */
+
+static char *
+get_cwe_url (int cwe)
+{
+  return xasprintf ("https://cwe.mitre.org/data/definitions/%i.html", cwe);
+}
+
+/* If DIAGNOSTIC has metadata, print it.
+
+   For example, if the diagnostic metadata associates it with CWE-119,
+   " [CWE-119]" will be printed, suitably colorized, and with a URL of a
+   description of the security issue.  */
+
+static void
+print_any_metadata (diagnostic_context *context,
+		    const diagnostic_info *diagnostic)
+{
+  if (diagnostic->metadata == NULL)
+    return;
+
+  int cwe = diagnostic->metadata->get_cwe ();
+  if (cwe)
+    {
+      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 *cwe_url = get_cwe_url (cwe);
+      pp_begin_url (pp, cwe_url);
+      free (cwe_url);
+      pp_printf (pp, "CWE-%i", cwe);
+      pp_set_prefix (context->printer, saved_prefix);
+      pp_end_url (pp);
+      pp_string (pp, colorize_stop (pp_show_color (pp)));
+      pp_character (pp, ']');
+    }
+}
+
 /* Print any metadata about the option used to control DIAGNOSTIC to CONTEXT's
    printer, e.g. " [-Werror=uninitialized]".
    Subroutine of diagnostic_report_diagnostic.  */
@@ -1059,6 +1105,8 @@  diagnostic_report_diagnostic (diagnostic_context *context,
   pp_format (context->printer, &diagnostic->message);
   (*diagnostic_starter (context)) (context, diagnostic);
   pp_output_formatted_text (context->printer);
+  if (context->show_metadata)
+    print_any_metadata (context, diagnostic);
   if (context->show_option_requested)
     print_option_information (context, diagnostic, orig_diag_kind);
   (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);
@@ -1190,8 +1238,8 @@  diagnostic_append_note (diagnostic_context *context,
    permerror, error, error_at, error_at, sorry, fatal_error, internal_error,
    and internal_error_no_backtrace, as documented and defined below.  */
 static bool
-diagnostic_impl (rich_location *richloc, int opt,
-		 const char *gmsgid,
+diagnostic_impl (rich_location *richloc, const diagnostic_metadata *metadata,
+		 int opt, const char *gmsgid,
 		 va_list *ap, diagnostic_t kind)
 {
   diagnostic_info diagnostic;
@@ -1207,13 +1255,15 @@  diagnostic_impl (rich_location *richloc, int opt,
       if (kind == DK_WARNING || kind == DK_PEDWARN)
 	diagnostic.option_index = opt;
     }
+  diagnostic.metadata = metadata;
   return diagnostic_report_diagnostic (global_dc, &diagnostic);
 }
 
 /* Implement inform_n, warning_n, and error_n, as documented and
    defined below.  */
 static bool
-diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
+diagnostic_n_impl (rich_location *richloc, const diagnostic_metadata *metadata,
+		   int opt, unsigned HOST_WIDE_INT n,
 		   const char *singular_gmsgid,
 		   const char *plural_gmsgid,
 		   va_list *ap, diagnostic_t kind)
@@ -1233,6 +1283,7 @@  diagnostic_n_impl (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
   diagnostic_set_info_translated (&diagnostic, text, ap, richloc, kind);
   if (kind == DK_WARNING)
     diagnostic.option_index = opt;
+  diagnostic.metadata = metadata;
   return diagnostic_report_diagnostic (global_dc, &diagnostic);
 }
 
@@ -1246,7 +1297,7 @@  emit_diagnostic (diagnostic_t kind, location_t location, int opt,
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, location);
-  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, kind);
+  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, kind);
   va_end (ap);
   return ret;
 }
@@ -1260,7 +1311,7 @@  emit_diagnostic (diagnostic_t kind, rich_location *richloc, int opt,
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, kind);
+  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, kind);
   va_end (ap);
   return ret;
 }
@@ -1272,7 +1323,18 @@  emit_diagnostic_valist (diagnostic_t kind, location_t location, int opt,
 			const char *gmsgid, va_list *ap)
 {
   rich_location richloc (line_table, location);
-  return diagnostic_impl (&richloc, opt, gmsgid, ap, kind);
+  return diagnostic_impl (&richloc, NULL, opt, gmsgid, ap, kind);
+}
+
+/* Wrapper around diagnostic_impl taking a va_list parameter.  */
+
+bool
+emit_diagnostic_valist (diagnostic_t kind, rich_location *richloc,
+			const diagnostic_metadata *metadata,
+			int opt,
+			const char *gmsgid, va_list *ap)
+{
+  return diagnostic_impl (richloc, metadata, opt, gmsgid, ap, kind);
 }
 
 /* An informative note at LOCATION.  Use this for additional details on an error
@@ -1284,7 +1346,7 @@  inform (location_t location, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, location);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_NOTE);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
   va_end (ap);
 }
 
@@ -1297,7 +1359,7 @@  inform (rich_location *richloc, const char *gmsgid, ...)
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_NOTE);
+  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_NOTE);
   va_end (ap);
 }
 
@@ -1311,7 +1373,7 @@  inform_n (location_t location, unsigned HOST_WIDE_INT n,
   va_start (ap, plural_gmsgid);
   auto_diagnostic_group d;
   rich_location richloc (line_table, location);
-  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid,
+  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid,
 		     &ap, DK_NOTE);
   va_end (ap);
 }
@@ -1326,7 +1388,7 @@  warning (int opt, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, input_location);
-  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING);
+  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
   va_end (ap);
   return ret;
 }
@@ -1342,7 +1404,7 @@  warning_at (location_t location, int opt, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, location);
-  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_WARNING);
+  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
   va_end (ap);
   return ret;
 }
@@ -1357,7 +1419,25 @@  warning_at (rich_location *richloc, int opt, const char *gmsgid, ...)
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_WARNING);
+  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_WARNING);
+  va_end (ap);
+  return ret;
+}
+
+/* Same as "warning at" above, but using METADATA.  */
+
+bool
+warning_at (rich_location *richloc, const diagnostic_metadata &metadata,
+	    int opt, const char *gmsgid, ...)
+{
+  gcc_assert (richloc);
+
+  auto_diagnostic_group d;
+  va_list ap;
+  va_start (ap, gmsgid);
+  bool ret
+    = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap,
+		       DK_WARNING);
   va_end (ap);
   return ret;
 }
@@ -1373,7 +1453,7 @@  warning_n (rich_location *richloc, int opt, unsigned HOST_WIDE_INT n,
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, plural_gmsgid);
-  bool ret = diagnostic_n_impl (richloc, opt, n,
+  bool ret = diagnostic_n_impl (richloc, NULL, opt, n,
 				singular_gmsgid, plural_gmsgid,
 				&ap, DK_WARNING);
   va_end (ap);
@@ -1392,7 +1472,7 @@  warning_n (location_t location, int opt, unsigned HOST_WIDE_INT n,
   va_list ap;
   va_start (ap, plural_gmsgid);
   rich_location richloc (line_table, location);
-  bool ret = diagnostic_n_impl (&richloc, opt, n,
+  bool ret = diagnostic_n_impl (&richloc, NULL, opt, n,
 				singular_gmsgid, plural_gmsgid,
 				&ap, DK_WARNING);
   va_end (ap);
@@ -1419,7 +1499,7 @@  pedwarn (location_t location, int opt, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, location);
-  bool ret = diagnostic_impl (&richloc, opt, gmsgid, &ap, DK_PEDWARN);
+  bool ret = diagnostic_impl (&richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN);
   va_end (ap);
   return ret;
 }
@@ -1434,7 +1514,7 @@  pedwarn (rich_location *richloc, int opt, const char *gmsgid, ...)
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_PEDWARN);
+  bool ret = diagnostic_impl (richloc, NULL, opt, gmsgid, &ap, DK_PEDWARN);
   va_end (ap);
   return ret;
 }
@@ -1453,7 +1533,7 @@  permerror (location_t location, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, location);
-  bool ret = diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_PERMERROR);
+  bool ret = diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR);
   va_end (ap);
   return ret;
 }
@@ -1468,7 +1548,7 @@  permerror (rich_location *richloc, const char *gmsgid, ...)
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  bool ret = diagnostic_impl (richloc, -1, gmsgid, &ap, DK_PERMERROR);
+  bool ret = diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_PERMERROR);
   va_end (ap);
   return ret;
 }
@@ -1482,7 +1562,7 @@  error (const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, input_location);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
   va_end (ap);
 }
 
@@ -1496,7 +1576,7 @@  error_n (location_t location, unsigned HOST_WIDE_INT n,
   va_list ap;
   va_start (ap, plural_gmsgid);
   rich_location richloc (line_table, location);
-  diagnostic_n_impl (&richloc, -1, n, singular_gmsgid, plural_gmsgid,
+  diagnostic_n_impl (&richloc, NULL, -1, n, singular_gmsgid, plural_gmsgid,
 		     &ap, DK_ERROR);
   va_end (ap);
 }
@@ -1509,7 +1589,7 @@  error_at (location_t loc, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, loc);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ERROR);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
   va_end (ap);
 }
 
@@ -1523,7 +1603,7 @@  error_at (rich_location *richloc, const char *gmsgid, ...)
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
-  diagnostic_impl (richloc, -1, gmsgid, &ap, DK_ERROR);
+  diagnostic_impl (richloc, NULL, -1, gmsgid, &ap, DK_ERROR);
   va_end (ap);
 }
 
@@ -1537,7 +1617,7 @@  sorry (const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, input_location);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
   va_end (ap);
 }
 
@@ -1549,7 +1629,7 @@  sorry_at (location_t loc, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, loc);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_SORRY);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_SORRY);
   va_end (ap);
 }
 
@@ -1571,7 +1651,7 @@  fatal_error (location_t loc, const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, loc);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_FATAL);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_FATAL);
   va_end (ap);
 
   gcc_unreachable ();
@@ -1588,7 +1668,7 @@  internal_error (const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, input_location);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE);
   va_end (ap);
 
   gcc_unreachable ();
@@ -1604,7 +1684,7 @@  internal_error_no_backtrace (const char *gmsgid, ...)
   va_list ap;
   va_start (ap, gmsgid);
   rich_location richloc (line_table, input_location);
-  diagnostic_impl (&richloc, -1, gmsgid, &ap, DK_ICE_NOBT);
+  diagnostic_impl (&richloc, NULL, -1, gmsgid, &ap, DK_ICE_NOBT);
   va_end (ap);
 
   gcc_unreachable ();
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 2fb75e6..0e4b6ad 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -46,6 +46,10 @@  struct diagnostic_info
   /* The location at which the diagnostic is to be reported.  */
   rich_location *richloc;
 
+  /* An optional bundle of metadata associated with the diagnostic
+     (or NULL).  */
+  const diagnostic_metadata *metadata;
+
   /* Auxiliary data for client.  */
   void *x_data;
   /* The kind of diagnostic it is about.  */
@@ -126,6 +130,9 @@  struct diagnostic_context
   /* Character used for caret diagnostics.  */
   char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
 
+  /* True if we should print any metadata associated with diagnostics.  */
+  bool show_metadata;
+
   /* True if we should print the command line option which controls
      each diagnostic, if known.  */
   bool show_option_requested;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index fc92913..209e2c6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -279,6 +279,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
 -fdiagnostics-nn-line-numbers @gol
+-fno-diagnostics-show-metadata  @gol
 -fdiagnostics-minimum-margin-width=@var{width} @gol
 -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
 -fdiagnostics-show-template-tree  -fno-elide-type @gol
@@ -4012,6 +4013,13 @@  as the types of expressions:
 This option suppresses the printing of these labels (in the example above,
 the vertical bars and the ``char *'' and ``long int'' text).
 
+@item -fno-diagnostics-show-metadata
+@opindex fno-diagnostics-show-metadata
+@opindex fdiagnostics-show-metadata
+Diagnostics can optionally contain @url{https://cwe.mitre.org/index.html, CWE}
+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-line-numbers
 @opindex fno-diagnostics-show-line-numbers
 @opindex fdiagnostics-show-line-numbers
diff --git a/gcc/opts.c b/gcc/opts.c
index 9928d01..0caaf04 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2491,6 +2491,10 @@  common_handle_option (struct gcc_options *opts,
       dc->parseable_fixits_p = value;
       break;
 
+    case OPT_fdiagnostics_show_metadata:
+      dc->show_metadata = value;
+      break;
+
     case OPT_fdiagnostics_show_option:
       dc->show_option_requested = value;
       break;
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 2d0a390..7136164 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1102,6 +1102,8 @@  general_init (const char *argv0, bool init_signals)
     = global_options_init.x_flag_diagnostics_show_line_numbers;
   global_dc->use_nn_for_line_numbers_p
     = global_options_init.x_flag_diagnostics_nn_line_numbers;
+  global_dc->show_metadata
+    = global_options_init.x_flag_diagnostics_show_metadata;
   global_dc->show_option_requested
     = global_options_init.x_flag_diagnostics_show_option;
   global_dc->min_margin_width