From 08092e704386de1f2cb83351790ab13be61536e1 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 4 Mar 2019 17:09:00 -0500
Subject: [PATCH 50/87] FIXME: start adding metadata

---
 .../diagnostic-json-to-html-templates/index.html   |   5 +
 gcc/analyzer/engine.cc                             |   3 +-
 gcc/analyzer/hardcoded-state-machine.cc            |  51 ++++++---
 gcc/analyzer/hardcoded-state-machine.h             |  13 +++
 gcc/common.opt                                     |   4 +
 gcc/diagnostic-core.h                              |  10 +-
 gcc/diagnostic-format-json.cc                      |  24 +++++
 gcc/diagnostic-metadata.h                          |  44 ++++++++
 gcc/diagnostic.c                                   | 114 +++++++++++++++------
 gcc/diagnostic.h                                   |   6 ++
 gcc/opts.c                                         |   4 +
 .../script.py                                      |  37 -------
 .../stderr.txt                                     |   6 --
 gcc/toplev.c                                       |   2 +
 14 files changed, 230 insertions(+), 93 deletions(-)
 create mode 100644 gcc/diagnostic-metadata.h
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/script.py
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/stderr.txt

diff --git a/contrib/diagnostic-json-to-html-templates/index.html b/contrib/diagnostic-json-to-html-templates/index.html
index e1d3cab..8f107b1 100644
--- a/contrib/diagnostic-json-to-html-templates/index.html
+++ b/contrib/diagnostic-json-to-html-templates/index.html
@@ -73,6 +73,11 @@ badge badge-pill badge-info
 	<span>: </span>
 	<span>{{ d['message'] }}</span>
       </a>
+      {% if d['metadata'] %}
+      {% if d['metadata']['cwe'] %}
+      <span>[<a href="https://cwe.mitre.org/data/definitions/{{d['metadata']['cwe']}}.html">CWE-{{d['metadata']['cwe']}}</a>]</span>
+      {% endif %}
+      {% endif %}
     </h5>
     <span><a href="{{d['option_url']}}">{{ d['option'] }}</a></span>
   </div>
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 9cc27d3..7298a7e 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -630,6 +630,7 @@ public:
 
   void error (const supernode *node, const gimple *stmt,
 	      tree var, state_machine::state_t state,
+	      const metadata *m,
 	      const char *fmt, ...) FINAL OVERRIDE
   {
     JIT_LOG_FUNC (m_engine->get_logger ());
@@ -652,7 +653,7 @@ public:
 	push_cfun (node->m_fun);
 	va_list ap;
 	va_start (ap, fmt);
-	emit_diagnostic_valist (DK_ERROR, &rich_loc, 0, fmt,
+	emit_diagnostic_valist (DK_ERROR, &rich_loc, m, 0, fmt,
 				&ap);
 	va_end (ap);
 	pop_cfun ();
diff --git a/gcc/analyzer/hardcoded-state-machine.cc b/gcc/analyzer/hardcoded-state-machine.cc
index a25a7e5..1b81e07 100644
--- a/gcc/analyzer/hardcoded-state-machine.cc
+++ b/gcc/analyzer/hardcoded-state-machine.cc
@@ -160,7 +160,10 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	     we don't want want to complain about double-free of NULL.  */
 
 	  /* free -> stop, with error.  */
-          sm_ctxt->error (node, stmt, arg, m_free, "double-free of %qE", arg);
+	  metadata m;
+	  m.add_cwe (415); /* CWE-415: Double Free.  */
+	  sm_ctxt->error (node, stmt, arg, m_free, &m,
+			  "double-free of %qE", arg);
 	  sm_ctxt->on_transition (node, stmt, arg, m_free, m_stop);
 	}
     }
@@ -193,14 +196,27 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
       if (TREE_CODE (op) == MEM_REF)
 	{
 	  tree arg = TREE_OPERAND (op, 0);
-	  sm_ctxt->error (node, stmt, arg, m_unchecked, "use of possibly-NULL %qE", arg);
-	  sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
 
-	  sm_ctxt->error (node, stmt, arg, m_null, "use of NULL %qE", arg);
-	  sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
+	  {
+	    /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
+	    metadata m;
+	    m.add_cwe (690);
+	    sm_ctxt->error (node, stmt, arg, m_unchecked, &m,
+			    "use of possibly-NULL %qE", arg);
+	    sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
+
+	    sm_ctxt->error (node, stmt, arg, m_null, &m,
+			    "use of NULL %qE", arg);
+	    sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
+	  }
 
-	  sm_ctxt->error (node, stmt, arg, m_free, "use after free of %qE", arg);
-	  sm_ctxt->on_transition (node, stmt, arg, m_free, m_stop);
+	  {
+	    metadata m;
+	    m.add_cwe (416); /* CWE-416: Use After Free.  */
+	    sm_ctxt->error (node, stmt, arg, m_free, &m,
+			    "use after free of %qE", arg);
+	    sm_ctxt->on_transition (node, stmt, arg, m_free, m_stop);
+	  }
 	}
     }
 }
@@ -255,8 +271,10 @@ malloc_state_machine::on_leak (sm_context *sm_ctxt,
 			       tree var,
 			       state_machine::state_t state) const
 {
-  sm_ctxt->error (node, stmt, var, m_unchecked, "leak of %qE", var);
-  sm_ctxt->error (node, stmt, var, m_nonnull, "leak of %qE", var);
+  metadata m;
+  m.add_cwe (401);
+  sm_ctxt->error (node, stmt, var, m_unchecked, &m, "leak of %qE", var);
+  sm_ctxt->error (node, stmt, var, m_nonnull, &m, "leak of %qE", var);
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -309,7 +327,8 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 
 	  sm_ctxt->on_transition (node, stmt , arg, m_nonnull, m_closed);
 
-          sm_ctxt->error (node, stmt, arg, m_closed, "double %<fclose%> of FILE %qE", arg);
+          sm_ctxt->error (node, stmt, arg, m_closed, NULL,
+			  "double %<fclose%> of FILE %qE", arg);
 	  sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
 	}
 
@@ -369,8 +388,12 @@ fileptr_state_machine::on_leak (sm_context *sm_ctxt,
 				tree var,
 				state_machine::state_t state) const
 {
-  sm_ctxt->error (node, stmt, var, m_unchecked, "leak of FILE %qE", var);
-  sm_ctxt->error (node, stmt, var, m_nonnull, "leak of FILE %qE", var);
+  metadata m;
+  m.add_cwe (775);
+  sm_ctxt->error (node, stmt, var, m_unchecked, &m,
+		  "leak of FILE %qE", var);
+  sm_ctxt->error (node, stmt, var, m_nonnull, &m,
+		  "leak of FILE %qE", var);
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -434,7 +457,9 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
       if (op == ARRAY_REF)
 	{
 	  tree arg = TREE_OPERAND (rhs1, 1);
-	  sm_ctxt->error (node, stmt, arg, m_tainted,
+	  metadata m;
+	  m.add_cwe (129);
+	  sm_ctxt->error (node, stmt, arg, m_tainted, &m,
 			  "use of tainted value %qE in array lookup without bounds checking",
 			  arg);
 	  sm_ctxt->on_transition (node, stmt, arg, m_tainted, m_stop);
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index a4bc713..df9c36c 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -1,4 +1,5 @@
 #include "analyzer-logging.h"
+#include "diagnostic-metadata.h"
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -45,6 +46,17 @@ private:
 
 ////////////////////////////////////////////////////////////////////////////
 
+/* FIXME.  */
+
+// TODO: maybe capture which sm emitted the error
+
+class metadata : public diagnostic_metadata
+{
+ public:
+
+ private:
+};
+
 /* Interface for the state machine to call the checker engine, at a
    particular stmt.  */
 
@@ -66,6 +78,7 @@ public:
   //virtual void transition (tree var, state_machine::state_t to) = 0;
   virtual void error (const supernode *node, const gimple *stmt,
 		      tree var, state_machine::state_t state,
+		      const metadata *m,
 		      const char *fmt, ...) = 0;
 
   // etc, what else?
diff --git a/gcc/common.opt b/gcc/common.opt
index 6cae3d2..f2b04e0 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1321,6 +1321,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)
+FIXME; TODO: add to docs
+
 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 aeb285f..1fd8f40 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 *);
@@ -109,9 +112,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 *, 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 a99b431..01dba54 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"
 
 /* The top-level JSON array of pending diagnostics.  */
@@ -99,6 +100,20 @@ json_from_fixit_hint (const fixit_hint *hint)
   return fixit_obj;
 }
 
+/* FIXME.  */
+
+static json::object *
+json_from_metadata (const diagnostic_metadata *metadata)
+{
+  json::object *metadata_obj = new json::object ();
+
+  // TODO:
+  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
@@ -214,6 +229,12 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
       json::value *path_value = context->make_json_for_path (context, path);
       diag_obj->set ("path", path_value);
     }
+
+  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.  */
@@ -272,6 +293,9 @@ diagnostic_output_format_init (diagnostic_context *context,
 	context->final_cb =  json_final_cb;
 	context->print_path = NULL; /* handled in json_end_diagnostic.  */
 
+	/* 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..2103971
--- /dev/null
+++ b/gcc/diagnostic-metadata.h
@@ -0,0 +1,44 @@
+/* Arbitrary key-value pairs 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
+
+/* FIXME.  */
+
+class diagnostic_metadata
+{
+ public:
+  diagnostic_metadata () : m_cwe (0) {}
+
+  void add_cwe (int cwe) { m_cwe = cwe; }
+  int get_cwe () const { return m_cwe; }
+
+  // TODO: how to store?
+ private:
+  int m_cwe;
+#if 0  
+  typedef hash_map <char *, char *,
+    simple_hashmap_traits<nofree_string_hash, char *> > map_t;
+  map_t m_map;
+#endif
+};
+
+#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index dd4712d..a0f8fa6 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backtrace.h"
 #include "diagnostic.h"
 #include "diagnostic-color.h"
+#include "diagnostic-metadata.h"
 #include "diagnostic-path.h"
 #include "edit-context.h"
 #include "selftest.h"
@@ -50,11 +51,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;
@@ -175,6 +178,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;
@@ -291,6 +295,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;
 }
@@ -895,6 +900,43 @@ update_effective_level_from_pragmas (diagnostic_context *context,
   return diag_class;
 }
 
+/* FIXME.  */
+
+static char *
+get_cwe_url (int cwe)
+{
+  return xasprintf ("https://cwe.mitre.org/data/definitions/%i.html", cwe);
+}
+
+/* FIXME.  */
+
+static void
+print_any_metadata (diagnostic_context *context,
+		    const diagnostic_info *diagnostic)
+{
+  if (diagnostic->metadata == NULL)
+    return;
+
+  // FIXME:
+  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.  */
@@ -1054,6 +1096,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);
@@ -1180,8 +1224,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;
@@ -1197,13 +1241,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)
@@ -1236,7 +1282,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;
 }
@@ -1250,7 +1296,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;
 }
@@ -1262,16 +1308,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, int opt,
+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, opt, gmsgid, ap, kind);
+  return diagnostic_impl (richloc, metadata, opt, gmsgid, ap, kind);
 }
 
 /* An informative note at LOCATION.  Use this for additional details on an error
@@ -1283,7 +1331,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);
 }
 
@@ -1296,7 +1344,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);
 }
 
@@ -1310,7 +1358,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);
 }
@@ -1325,7 +1373,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;
 }
@@ -1341,7 +1389,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;
 }
@@ -1356,7 +1404,7 @@ 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;
 }
@@ -1372,7 +1420,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);
@@ -1391,7 +1439,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);
@@ -1418,7 +1466,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;
 }
@@ -1433,7 +1481,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;
 }
@@ -1452,7 +1500,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;
 }
@@ -1467,7 +1515,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;
 }
@@ -1481,7 +1529,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);
 }
 
@@ -1495,7 +1543,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);
 }
@@ -1508,7 +1556,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);
 }
 
@@ -1522,7 +1570,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);
 }
 
@@ -1536,7 +1584,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);
 }
 
@@ -1548,7 +1596,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);
 }
 
@@ -1570,7 +1618,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 ();
@@ -1587,7 +1635,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 ();
@@ -1603,7 +1651,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 cb4a1cf..b1d3e11 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -46,6 +46,9 @@ struct diagnostic_info
   /* The location at which the diagnostic is to be reported.  */
   rich_location *richloc;
 
+  /* FIXME.  */
+  const diagnostic_metadata *metadata;
+
   /* Auxiliary data for client.  */
   void *x_data;
   /* The kind of diagnostic it is about.  */
@@ -127,6 +130,9 @@ struct diagnostic_context
   /* Character used for caret diagnostics.  */
   char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
 
+  /* FIXME.  */
+  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/opts.c b/gcc/opts.c
index f0a96b9..fcf2685 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2400,6 +2400,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/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/script.py
deleted file mode 100644
index 096ac60..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/script.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-#   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-#   Copyright 2012 Red Hat, Inc.
-#
-#   This 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 of the License, or
-#   (at your option) any later version.
-#
-#   This program 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 this program.  If not, see
-#   <http://www.gnu.org/licenses/>.
-
-from sm import main
-from sm.parser import parse_file
-
-def selftest(ctxt, solution):
-    if 0:
-        import sys
-        solution.dump(sys.stderr)
-
-    # Verify that the:
-    #   int *p = calls_malloc();
-    # transitions "p" from "ptr.start" to "ptr.unknown"
-    node = ctxt.find_call_of('calls_malloc')
-    ctxt.assert_statenames_for_varname(node, 'p', {'ptr.start'})
-
-    node = ctxt.get_intraprocedural_successor(node)
-    ctxt.assert_statenames_for_varname(node, 'p', {'ptr.unchecked'})
-
-checker = parse_file('sm/checkers/malloc_checker.sm')
-main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/stderr.txt
deleted file mode 100644
index 338e9b4..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/callsite-lhs-inherits-from-return-val/stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c: In function 'test':
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c:32:nn: error: dereference of possibly-NULL pointer p [CWE-690]
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c:30:nn: note: call from test() to calls_malloc()
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c:25:nn: note: return value assigned to the result of malloc()
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c:30:nn: note: return from calls_malloc() to test(): state of return value ("ptr.unchecked") propagated to p
-tests/sm/interprocedural/callsite-lhs-inherits-from-return-val/input.c:32:nn: note: dereference of possibly-NULL pointer p
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 6b3bbdc..2fdd2c8 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1119,6 +1119,8 @@ general_init (const char *argv0, bool init_signals)
     = global_options_init.x_flag_diagnostics_show_labels;
   global_dc->show_line_numbers_p
     = global_options_init.x_flag_diagnostics_show_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
-- 
1.8.5.3

