From 674247e9ca255a3d367445f14d4fc7e204f6641d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 8 Jun 2016 13:51:21 -0400
Subject: [PATCH 10/22] FIXME: implement -fdiagnostics-parseable-fixits (needs
 docs, testcases, ChangeLog)

---
 gcc/common.opt            |  4 +++
 gcc/diagnostic.c          | 91 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/diagnostic.h          |  4 +++
 gcc/opts.c                |  4 +++
 libcpp/include/line-map.h | 16 +++++++++
 5 files changed, 119 insertions(+)

diff --git a/gcc/common.opt b/gcc/common.opt
index f0d7196..5d90385 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1185,6 +1185,10 @@ Enum(diagnostic_color_rule) String(always) Value(DIAGNOSTICS_COLOR_YES)
 EnumValue
 Enum(diagnostic_color_rule) String(auto) Value(DIAGNOSTICS_COLOR_AUTO)
 
+fdiagnostics-parseable-fixits
+Common Var(flag_diagnostics_parseable_fixits)
+Print fixit hints in machine-readable form.
+
 fdiagnostics-show-option
 Common Var(flag_diagnostics_show_option) Init(1)
 Amend appropriate diagnostic messages with the command line option that controls them.
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 9f419fb..856e7fb 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -667,6 +667,95 @@ diagnostic_pop_diagnostics (diagnostic_context *context, location_t where)
   context->n_classification_history ++;
 }
 
+/* FIXME.  */
+
+static void
+print_escaped_string (FILE *out, const char *text)
+{
+  fputc ('"', out);
+  for (const char *ch = text; *ch; ch++)
+    {
+      switch (*ch)
+	{
+	case '\\':
+	  /* Escape backslash as two backslashes.  */
+	  fputs ("\\\\", out);
+	  break;
+	case '\t':
+	  /* Escape tab as "\t".  */
+	  fputs ("\\t", out);
+	  break;
+	case '\n':
+	  /* Escape newline as "\n".  */
+	  fputs ("\\n", out);
+	  break;
+	case '"':
+	  /* Escape doublequotes as \".  */
+	  fputs ("\\\"", out);
+	  break;
+	/* FIXME: use octal for non-printable chars.  */
+	default:
+	  fputc (*ch, out);
+	  break;
+	}
+    }
+  fputc ('"', out);
+}
+
+/* FIXME.  */
+
+static void
+print_parseable_fixits (FILE* out, diagnostic_info *diagnostic)
+{
+  rich_location *richloc = diagnostic->richloc;
+  for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
+    {
+      const fixit_hint *hint = richloc->get_fixit_hint (i);
+      source_location start_loc = hint->get_start_loc ();
+      expanded_location start_exploc = expand_location (start_loc);
+      fprintf (out, "fix-it:");
+      print_escaped_string (out, start_exploc.file);
+      source_location end_loc;
+      if (hint->maybe_get_end_loc (&end_loc))
+	{
+	  /* For compatibility with clang, print as a half-open range.  */
+	  expanded_location end_exploc = expand_location (end_loc);
+	  fprintf (out, ":{%i:%i-%i:%i}:",
+		   start_exploc.line, start_exploc.column,
+		   end_exploc.line, end_exploc.column - 1);
+	}
+      else
+	fprintf (out, ":{%i:%i}:",
+		 start_exploc.line,start_exploc.column);
+      switch (hint->get_kind ())
+	{
+	  case fixit_hint::INSERT:
+	    {
+	      const fixit_insert *insert
+		= static_cast <const fixit_insert *> (hint);
+	      print_escaped_string (out, insert->get_string ());
+	    }
+	    break;
+
+	  case fixit_hint::REMOVE:
+	    print_escaped_string (out, "");
+	    break;
+
+	  case fixit_hint::REPLACE:
+	    {
+	      const fixit_replace *replace
+		= static_cast <const fixit_replace *> (hint);
+	      print_escaped_string (out, replace->get_string ());
+	    }
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	}
+      fprintf (out, "\n");
+    }
+}
+
 /* Report a diagnostic message (an error or a warning) as specified by
    DC.  This function is *the* subroutine in terms of which front-ends
    should implement their specific diagnostic handling modules.  The
@@ -828,6 +917,8 @@ diagnostic_report_diagnostic (diagnostic_context *context,
   (*diagnostic_starter (context)) (context, diagnostic);
   pp_output_formatted_text (context->printer);
   (*diagnostic_finalizer (context)) (context, diagnostic);
+  if (context->parseable_fixits_p)
+    print_parseable_fixits (stderr, diagnostic);
   diagnostic_action_after_output (context, diagnostic->kind);
   diagnostic->message.format_spec = saved_format_spec;
   diagnostic->x_data = NULL;
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 58d77df..afce285 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -205,6 +205,10 @@ struct diagnostic_context
   /* Usable by plugins; if true, print a debugging ruler above the
      source output.  */
   bool show_ruler_p;
+
+  /* If true, print fixits in machine-parseable form after the
+     rest of the diagnostic.  */
+  bool parseable_fixits_p;
 };
 
 static inline void
diff --git a/gcc/opts.c b/gcc/opts.c
index e80331f..7406210 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1871,6 +1871,10 @@ common_handle_option (struct gcc_options *opts,
       diagnostic_color_init (dc, value);
       break;
 
+    case OPT_fdiagnostics_parseable_fixits:
+      dc->parseable_fixits_p = value;
+      break;
+
     case OPT_fdiagnostics_show_option:
       dc->show_option_requested = value;
       break;
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 292abce..416419c 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1418,6 +1418,8 @@ public:
 
   virtual enum kind get_kind () const = 0;
   virtual bool affects_line_p (const char *file, int line) = 0;
+  virtual source_location get_start_loc () const = 0;
+  virtual bool maybe_get_end_loc (source_location *out) const = 0;
 };
 
 class fixit_insert : public fixit_hint
@@ -1428,6 +1430,8 @@ class fixit_insert : public fixit_hint
   ~fixit_insert ();
   enum kind get_kind () const { return INSERT; }
   bool affects_line_p (const char *file, int line);
+  source_location get_start_loc () const { return m_where; }
+  bool maybe_get_end_loc (source_location *) const { return false; }
 
   source_location get_location () const { return m_where; }
   const char *get_string () const { return m_bytes; }
@@ -1447,6 +1451,12 @@ class fixit_remove : public fixit_hint
 
   enum kind get_kind () const { return REMOVE; }
   bool affects_line_p (const char *file, int line);
+  source_location get_start_loc () const { return m_src_range.m_start; }
+  bool maybe_get_end_loc (source_location *out) const
+  {
+    *out = m_src_range.m_finish;
+    return true;
+  }
 
   source_range get_range () const { return m_src_range; }
 
@@ -1463,6 +1473,12 @@ class fixit_replace : public fixit_hint
 
   enum kind get_kind () const { return REPLACE; }
   bool affects_line_p (const char *file, int line);
+  source_location get_start_loc () const { return m_src_range.m_start; }
+  bool maybe_get_end_loc (source_location *out) const
+  {
+    *out = m_src_range.m_finish;
+    return true;
+  }
 
   source_range get_range () const { return m_src_range; }
   const char *get_string () const { return m_bytes; }
-- 
1.8.5.3

