diff mbox series

[committed] Add -fdiagnostics-format={json-stderr|json-file}

Message ID 20220602194521.843639-1-dmalcolm@redhat.com
State New
Headers show
Series [committed] Add -fdiagnostics-format={json-stderr|json-file} | expand

Commit Message

David Malcolm June 2, 2022, 7:45 p.m. UTC
This commit adds -fdiagnostics-format=json-file, writing to
DUMP_BASE_NAME.gcc.json, and adds -fdiagnostics-format=json-stderr,
a synonym for the existing -fdiagnostics-format=json.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r13-966-g5ab73173cca461.

gcc/ChangeLog:
	* common.opt (fdiagnostics-format=): Add json-stderr and json-file
	to description.
	(DIAGNOSTICS_OUTPUT_FORMAT_JSON): Rename to...
	(DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR): ...this.
	(diagnostics_output_format): Add json-stderr and json-file.
	* diagnostic-format-json.cc (json_flush_to_file): New.
	(json_final_cb): Convert to...
	(json_flush_to_file): ...this, ...
	(json_stderr_final_cb): ...this, and...
	(json_file_final_cb): ...this.
	(diagnostic_output_format_init): Move to diagnostic.cc.
	(json_output_base_file_name): New.
	(diagnostic_output_format_init_json): New.
	(diagnostic_output_format_init_json_stderr): New.
	(diagnostic_output_format_init_json_file): New.
	* diagnostic.cc (diagnostic_output_format_init): Move here from
	diagnostic-format-json.cc; update for changes to enum.
	* diagnostic.h (enum diagnostics_output_format): Rename
	DIAGNOSTICS_OUTPUT_FORMAT_JSON to
	DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR, and add
	DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE.
	(diagnostic_output_format_init): Add base_file_name param.
	(diagnostic_output_format_init_json_stderr): New decl.
	(diagnostic_output_format_init_json_file): New dec.
	* doc/invoke.texi (-fdiagnostics-format=): Add "json-stderr" and
	"json-file".  Rewrite so that the existing "json" is a synonym of
	"json-stderr".
	* gcc.cc (driver_handle_option): Pass dump_base_name to
	diagnostic_output_format_init.
	* opts.cc (common_handle_option): Likewise.

gcc/testsuite/ChangeLog:
	* c-c++-common/diagnostic-format-json-file-1.c: New test.
	* c-c++-common/diagnostic-format-json-stderr-1.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/common.opt                                |  10 +-
 gcc/diagnostic-format-json.cc                 | 116 ++++++++++++------
 gcc/diagnostic.cc                             |  26 ++++
 gcc/diagnostic.h                              |  11 +-
 gcc/doc/invoke.texi                           |  17 ++-
 gcc/gcc.cc                                    |   2 +-
 gcc/opts.cc                                   |   2 +-
 .../diagnostic-format-json-file-1.c           |   8 ++
 .../diagnostic-format-json-stderr-1.c         |  33 +++++
 9 files changed, 173 insertions(+), 52 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/diagnostic-format-json-file-1.c
 create mode 100644 gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
diff mbox series

Patch

diff --git a/gcc/common.opt b/gcc/common.opt
index 8a0dafc522d..3237ce9b530 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1390,7 +1390,7 @@  Common Joined RejectNegative UInteger
 
 fdiagnostics-format=
 Common Joined RejectNegative Enum(diagnostics_output_format)
--fdiagnostics-format=[text|json]	Select output format.
+-fdiagnostics-format=[text|json|json-stderr|json-file]	Select output format.
 
 fdiagnostics-escape-format=
 Common Joined RejectNegative Enum(diagnostics_escape_format)
@@ -1425,7 +1425,13 @@  EnumValue
 Enum(diagnostics_output_format) String(text) Value(DIAGNOSTICS_OUTPUT_FORMAT_TEXT)
 
 EnumValue
-Enum(diagnostics_output_format) String(json) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON)
+Enum(diagnostics_output_format) String(json) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR)
+
+EnumValue
+Enum(diagnostics_output_format) String(json-stderr) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR)
+
+EnumValue
+Enum(diagnostics_output_format) String(json-file) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE)
 
 fdiagnostics-parseable-fixits
 Common Var(flag_diagnostics_parseable_fixits)
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index 62594ebb4d7..051fa6c2e48 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -285,57 +285,93 @@  json_end_group (diagnostic_context *)
   cur_children_array = NULL;
 }
 
-/* Callback for final cleanup for JSON output.  */
+/* Flush the top-level array to OUTF.  */
 
 static void
-json_final_cb (diagnostic_context *)
+json_flush_to_file (FILE *outf)
 {
-  /* Flush the top-level array.  */
-  toplevel_array->dump (stderr);
-  fprintf (stderr, "\n");
+  toplevel_array->dump (outf);
+  fprintf (outf, "\n");
   delete toplevel_array;
   toplevel_array = NULL;
 }
 
-/* Set the output format for CONTEXT to FORMAT.  */
+/* Callback for final cleanup for JSON output to stderr.  */
 
-void
-diagnostic_output_format_init (diagnostic_context *context,
-			       enum diagnostics_output_format format)
+static void
+json_stderr_final_cb (diagnostic_context *)
+{
+  json_flush_to_file (stderr);
+}
+
+static char *json_output_base_file_name;
+
+/* Callback for final cleanup for JSON output to a file.  */
+
+static void
+json_file_final_cb (diagnostic_context *)
 {
-  switch (format)
+  char *filename = concat (json_output_base_file_name, ".gcc.json", NULL);
+  FILE *outf = fopen (filename, "w");
+  if (!outf)
     {
-    default:
-      gcc_unreachable ();
-    case DIAGNOSTICS_OUTPUT_FORMAT_TEXT:
-      /* The default; do nothing.  */
-      break;
-
-    case DIAGNOSTICS_OUTPUT_FORMAT_JSON:
-      {
-	/* Set up top-level JSON array.  */
-	if (toplevel_array == NULL)
-	  toplevel_array = new json::array ();
-
-	/* Override callbacks.  */
-	context->begin_diagnostic = json_begin_diagnostic;
-	context->end_diagnostic = json_end_diagnostic;
-	context->begin_group_cb = json_begin_group;
-	context->end_group_cb =  json_end_group;
-	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_cwe = false;
-
-	/* The option is handled in JSON format, rather than as text.  */
-	context->show_option_requested = false;
-
-	/* Don't colorize the text.  */
-	pp_show_color (context->printer) = false;
-      }
-      break;
+      const char *errstr = xstrerror (errno);
+      fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
+	       filename, errstr);
+      free (filename);
+      return;
     }
+  json_flush_to_file (outf);
+  fclose (outf);
+  free (filename);
+}
+
+/* Populate CONTEXT in preparation for JSON output (either to stderr, or
+   to a file).  */
+
+static void
+diagnostic_output_format_init_json (diagnostic_context *context)
+{
+  /* Set up top-level JSON array.  */
+  if (toplevel_array == NULL)
+    toplevel_array = new json::array ();
+
+  /* Override callbacks.  */
+  context->begin_diagnostic = json_begin_diagnostic;
+  context->end_diagnostic = json_end_diagnostic;
+  context->begin_group_cb = json_begin_group;
+  context->end_group_cb =  json_end_group;
+  context->print_path = NULL; /* handled in json_end_diagnostic.  */
+
+  /* The metadata is handled in JSON format, rather than as text.  */
+  context->show_cwe = false;
+
+  /* The option is handled in JSON format, rather than as text.  */
+  context->show_option_requested = false;
+
+  /* Don't colorize the text.  */
+  pp_show_color (context->printer) = false;
+}
+
+/* Populate CONTEXT in preparation for JSON output to stderr.  */
+
+void
+diagnostic_output_format_init_json_stderr (diagnostic_context *context)
+{
+  diagnostic_output_format_init_json (context);
+  context->final_cb = json_stderr_final_cb;
+}
+
+/* Populate CONTEXT in preparation for JSON output to a file named
+   BASE_FILE_NAME.gcc.json.  */
+
+void
+diagnostic_output_format_init_json_file (diagnostic_context *context,
+					 const char *base_file_name)
+{
+  diagnostic_output_format_init_json (context);
+  context->final_cb = json_file_final_cb;
+  json_output_base_file_name = xstrdup (base_file_name);
 }
 
 #if CHECKING_P
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index fef11467b6f..25504834484 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -2072,6 +2072,32 @@  auto_diagnostic_group::~auto_diagnostic_group ()
     }
 }
 
+/* Set the output format for CONTEXT to FORMAT, using BASE_FILE_NAME for
+   file-based output formats.  */
+
+void
+diagnostic_output_format_init (diagnostic_context *context,
+			       const char *base_file_name,
+			       enum diagnostics_output_format format)
+{
+  switch (format)
+    {
+    default:
+      gcc_unreachable ();
+    case DIAGNOSTICS_OUTPUT_FORMAT_TEXT:
+      /* The default; do nothing.  */
+      break;
+
+    case DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR:
+      diagnostic_output_format_init_json_stderr (context);
+      break;
+
+    case DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE:
+      diagnostic_output_format_init_json_file (context, base_file_name);
+      break;
+    }
+}
+
 /* Implementation of diagnostic_path::num_events vfunc for
    simple_diagnostic_path: simply get the number of events in the vec.  */
 
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 3ca32979dde..dd3af033ae4 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -59,8 +59,11 @@  enum diagnostics_output_format
   /* The default: textual output.  */
   DIAGNOSTICS_OUTPUT_FORMAT_TEXT,
 
-  /* JSON-based output.  */
-  DIAGNOSTICS_OUTPUT_FORMAT_JSON
+  /* JSON-based output, to stderr.  */
+  DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR,
+
+  /* JSON-based output, to a file.  */
+  DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE
 };
 
 /* An enum for controlling how diagnostic_paths should be printed.  */
@@ -577,7 +580,11 @@  extern char *file_name_as_prefix (diagnostic_context *, const char *);
 extern char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1;
 
 extern void diagnostic_output_format_init (diagnostic_context *,
+					   const char *base_file_name,
 					   enum diagnostics_output_format);
+extern void diagnostic_output_format_init_json_stderr (diagnostic_context *context);
+extern void diagnostic_output_format_init_json_file (diagnostic_context *context,
+						     const char *base_file_name);
 
 /* Compute the number of digits in the decimal representation of an integer.  */
 extern int num_digits (int);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 71098d86313..d85b66f60f0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -301,7 +301,7 @@  Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]}  @gol
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fdiagnostics-urls=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
--fdiagnostics-format=@r{[}text@r{|}json@r{]}  @gol
+-fdiagnostics-format=@r{[}text@r{|}json@r{|}json-stderr@r{|}json-file@r{]}  @gol
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
 -fno-diagnostics-show-labels  -fno-diagnostics-show-line-numbers @gol
 -fno-diagnostics-show-cwe  @gol
@@ -5305,14 +5305,19 @@  Unicode characters.  For the example above, the following will be printed:
 @item -fdiagnostics-format=@var{FORMAT}
 @opindex fdiagnostics-format
 Select a different format for printing diagnostics.
-@var{FORMAT} is @samp{text} or @samp{json}.
+@var{FORMAT} is @samp{text}, @samp{json}, @samp{json-stderr},
+or @samp{json-file}.
+
 The default is @samp{text}.
 
-The @samp{json} format consists of a top-level JSON array containing JSON
-objects representing the diagnostics.
+The @samp{json} format is a synonym for @samp{json-stderr}.
+The @samp{json-stderr} and @samp{json-file} formats are identical, apart from
+where the JSON is emitted to - with the former, the JSON is emitted to stderr,
+whereas with @samp{json-file} it is written to @file{@var{source}.gcc.json}.
 
-The JSON is emitted as one line, without formatting; the examples below
-have been formatted for clarity.
+The emitted JSON consists of a top-level JSON array containing JSON objects
+representing the diagnostics.  The JSON is emitted as one line, without
+formatting; the examples below have been formatted for clarity.
 
 Diagnostics can have child diagnostics.  For example, this error and note:
 
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 299e09c4f54..563f535d564 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -4335,7 +4335,7 @@  driver_handle_option (struct gcc_options *opts,
       break;
 
     case OPT_fdiagnostics_format_:
-      diagnostic_output_format_init (dc,
+      diagnostic_output_format_init (dc, opts->x_dump_base_name,
 				     (enum diagnostics_output_format)value);
       break;
 
diff --git a/gcc/opts.cc b/gcc/opts.cc
index f0c5c4db955..bf06a55456a 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2800,7 +2800,7 @@  common_handle_option (struct gcc_options *opts,
       break;
 
     case OPT_fdiagnostics_format_:
-      diagnostic_output_format_init (dc,
+      diagnostic_output_format_init (dc, opts->x_dump_base_name,
 				     (enum diagnostics_output_format)value);
       break;
 
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-file-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-file-1.c
new file mode 100644
index 00000000000..ddac780f07b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-file-1.c
@@ -0,0 +1,8 @@ 
+/* Check that -fdiagnostics-format=json-file works.  */
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=json-file" } */
+
+#warning message
+
+/* Verify that some JSON was written to a file with the expected name.  */
+/* { dg-final { scan-file "diagnostic-format-json-file-1.c.gcc.json" "\"message\": \"#warning message\"" } } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
new file mode 100644
index 00000000000..02f780bce10
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
@@ -0,0 +1,33 @@ 
+/* Check that "json" and "json-stderr" are synonymous when used as
+   arguments to "-fdiagnostics-format=".  */
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=json-stderr" } */
+
+#error message
+
+/* Use dg-regexp to consume the JSON output starting with
+   the innermost values, and working outwards.
+   We can't rely on any ordering of the keys.  */
+
+/* { dg-regexp "\"kind\": \"error\"" } */
+/* { dg-regexp "\"column-origin\": 1" } */
+/* { dg-regexp "\"escape-source\": false" } */
+/* { dg-regexp "\"message\": \"#error message\"" } */
+
+/* { dg-regexp "\"caret\": \{" } */
+/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-stderr-1.c\"" } */
+/* { dg-regexp "\"line\": 6" } */
+/* { dg-regexp "\"column\": 2" } */
+/* { dg-regexp "\"display-column\": 2" } */
+/* { dg-regexp "\"byte-column\": 2" } */
+
+/* { dg-regexp "\"finish\": \{" } */
+/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-stderr-1.c\"" } */
+/* { dg-regexp "\"line\": 6" } */
+/* { dg-regexp "\"column\": 6" } */
+/* { dg-regexp "\"display-column\": 6" } */
+/* { dg-regexp "\"byte-column\": 6" } */
+
+/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
+/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
+/* { dg-regexp "\[\[\{\}, \]*\]" } */