Add -fdiagnostics-format={json-stderr|json-file}

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.

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>
This commit is contained in:
David Malcolm 2022-06-02 15:38:38 -04:00
parent 4f9ad0b4b0
commit 5ab73173cc
9 changed files with 172 additions and 51 deletions

View file

@ -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)

View file

@ -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. */
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 *)
{
char *filename = concat (json_output_base_file_name, ".gcc.json", NULL);
FILE *outf = fopen (filename, "w");
if (!outf)
{
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 (diagnostic_context *context,
enum diagnostics_output_format format)
diagnostic_output_format_init_json_stderr (diagnostic_context *context)
{
switch (format)
{
default:
gcc_unreachable ();
case DIAGNOSTICS_OUTPUT_FORMAT_TEXT:
/* The default; do nothing. */
break;
diagnostic_output_format_init_json (context);
context->final_cb = json_stderr_final_cb;
}
case DIAGNOSTICS_OUTPUT_FORMAT_JSON:
{
/* Set up top-level JSON array. */
if (toplevel_array == NULL)
toplevel_array = new json::array ();
/* Populate CONTEXT in preparation for JSON output to a file named
BASE_FILE_NAME.gcc.json. */
/* 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;
}
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

View file

@ -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. */

View file

@ -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);

View file

@ -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:

View file

@ -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;

View file

@ -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;

View file

@ -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\"" } } */

View file

@ -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 "\[\[\{\}, \]*\]" } */