[11/49] Add diagnostic_metadata and CWE support
diff mbox series

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

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
>

Patch
diff mbox series

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