Commit 478dd60d by David Malcolm Committed by David Malcolm

Machine-readable diagnostic output (PR other/19165)

This patch implements a -fdiagnostics-format=json option which
converts the diagnostics to be output to stderr in a JSON format;
see the documentation in invoke.texi.

Logically-related diagnostics are nested at the JSON level, using
the auto_diagnostic_group mechanism.

gcc/ChangeLog:
	PR other/19165
	* Makefile.in (OBJS): Move json.o to...
	(OBJS-libcommon): ...here and add diagnostic-format-json.o.
	* common.opt (fdiagnostics-format=): New option.
	(diagnostics_output_format): New enum.
	* diagnostic-format-json.cc: New file.
	* diagnostic.c (default_diagnostic_final_cb): New function, taken
	from start of diagnostic_finish.
	(diagnostic_initialize): Initialize final_cb to
	default_diagnostic_final_cb.
	(diagnostic_finish): Move "being treated as errors" messages to
	default_diagnostic_final_cb.  Call any final_cb.
	(default_diagnostic_finalizer): Add diagnostic_t param.
	(diagnostic_report_diagnostic): Pass "orig_diag_kind" to
	diagnostic_finalizer callback.
	* diagnostic.h (enum diagnostics_output_format): New enum.
	(diagnostic_finalizer_fn): Reimplement, adding diagnostic_t param.
	(struct diagnostic_context): Add "final_cb".
	(default_diagnostic_finalizer): Add diagnostic_t param.
	(diagnostic_output_format_init): New decl.
	* doc/invoke.texi (-fdiagnostics-format): New option.
	* dwarf2out.c (gen_producer_string): Ignore
	OPT_fdiagnostics_format_.
	* gcc.c (driver_handle_option): Handle OPT_fdiagnostics_format_.
	* lto-wrapper.c (append_diag_options): Ignore it.
	* opts.c (common_handle_option): Handle it.

gcc/c-family/ChangeLog:
	PR other/19165
	* c-opts.c (c_diagnostic_finalizer): Add diagnostic_t param.

gcc/fortran/ChangeLog:
	PR other/19165
	* error.c (gfc_diagnostic_finalizer): Add diagnostic_t param.

gcc/jit/ChangeLog:
	PR other/19165
	* dummy-frontend.c (jit_begin_diagnostic): Add diagnostic_t param.

gcc/testsuite/ChangeLog:
	PR other/19165
	* c-c++-common/diagnostic-format-json-1.c: New test.
	* c-c++-common/diagnostic-format-json-2.c: New test.
	* c-c++-common/diagnostic-format-json-3.c: New test.
	* c-c++-common/diagnostic-format-json-4.c: New test.
	* c-c++-common/diagnostic-format-json-5.c: New test.
	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
	(custom_diagnostic_finalizer): Add diagnostic_t param.
	* gcc.dg/plugin/location_overflow_plugin.c
	(verify_unpacked_ranges): Likewise.
	(verify_no_columns): Likewise.
	* gfortran.dg/diagnostic-format-json-1.F90: New test.
	* gfortran.dg/diagnostic-format-json-2.F90: New test.
	* gfortran.dg/diagnostic-format-json-3.F90: New test.

From-SVN: r266186
parent ef190c93
2018-11-15 David Malcolm <dmalcolm@redhat.com> 2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
* Makefile.in (OBJS): Move json.o to...
(OBJS-libcommon): ...here and add diagnostic-format-json.o.
* common.opt (fdiagnostics-format=): New option.
(diagnostics_output_format): New enum.
* diagnostic-format-json.cc: New file.
* diagnostic.c (default_diagnostic_final_cb): New function, taken
from start of diagnostic_finish.
(diagnostic_initialize): Initialize final_cb to
default_diagnostic_final_cb.
(diagnostic_finish): Move "being treated as errors" messages to
default_diagnostic_final_cb. Call any final_cb.
(default_diagnostic_finalizer): Add diagnostic_t param.
(diagnostic_report_diagnostic): Pass "orig_diag_kind" to
diagnostic_finalizer callback.
* diagnostic.h (enum diagnostics_output_format): New enum.
(diagnostic_finalizer_fn): Reimplement, adding diagnostic_t param.
(struct diagnostic_context): Add "final_cb".
(default_diagnostic_finalizer): Add diagnostic_t param.
(diagnostic_output_format_init): New decl.
* doc/invoke.texi (-fdiagnostics-format): New option.
* dwarf2out.c (gen_producer_string): Ignore
OPT_fdiagnostics_format_.
* gcc.c (driver_handle_option): Handle OPT_fdiagnostics_format_.
* lto-wrapper.c (append_diag_options): Ignore it.
* opts.c (common_handle_option): Handle it.
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR tree-optimization/88015 PR tree-optimization/88015
* graphite-isl-ast-to-gimple.c * graphite-isl-ast-to-gimple.c
(translate_isl_ast_to_gimple::scop_to_isl_ast): Add missing check (translate_isl_ast_to_gimple::scop_to_isl_ast): Add missing check
...@@ -1395,7 +1395,6 @@ OBJS = \ ...@@ -1395,7 +1395,6 @@ OBJS = \
ira-color.o \ ira-color.o \
ira-emit.o \ ira-emit.o \
ira-lives.o \ ira-lives.o \
json.o \
jump.o \ jump.o \
langhooks.o \ langhooks.o \
lcm.o \ lcm.o \
...@@ -1619,6 +1618,7 @@ OBJS = \ ...@@ -1619,6 +1618,7 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with # Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies. # no target dependencies.
OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \ OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
diagnostic-format-json.o json.o \
edit-context.o \ edit-context.o \
pretty-print.o intl.o \ pretty-print.o intl.o \
sbitmap.o \ sbitmap.o \
......
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
* c-opts.c (c_diagnostic_finalizer): Add diagnostic_t param.
2018-11-14 Jakub Jelinek <jakub@redhat.com> 2018-11-14 Jakub Jelinek <jakub@redhat.com>
P1236R1 - Signed integers are two's complement P1236R1 - Signed integers are two's complement
......
...@@ -165,7 +165,8 @@ c_common_option_lang_mask (void) ...@@ -165,7 +165,8 @@ c_common_option_lang_mask (void)
/* Diagnostic finalizer for C/C++/Objective-C/Objective-C++. */ /* Diagnostic finalizer for C/C++/Objective-C/Objective-C++. */
static void static void
c_diagnostic_finalizer (diagnostic_context *context, c_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t)
{ {
diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind); diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
/* By default print macro expansion contexts in the diagnostic /* By default print macro expansion contexts in the diagnostic
......
...@@ -1277,6 +1277,23 @@ Enum(diagnostic_color_rule) String(always) Value(DIAGNOSTICS_COLOR_YES) ...@@ -1277,6 +1277,23 @@ Enum(diagnostic_color_rule) String(always) Value(DIAGNOSTICS_COLOR_YES)
EnumValue EnumValue
Enum(diagnostic_color_rule) String(auto) Value(DIAGNOSTICS_COLOR_AUTO) Enum(diagnostic_color_rule) String(auto) Value(DIAGNOSTICS_COLOR_AUTO)
fdiagnostics-format=
Common Joined RejectNegative Enum(diagnostics_output_format)
-fdiagnostics-format=[text|json] Select output format
; Required for these enum values.
SourceInclude
diagnostic.h
Enum
Name(diagnostics_output_format) Type(int)
EnumValue
Enum(diagnostics_output_format) String(text) Value(DIAGNOSTICS_OUTPUT_FORMAT_TEXT)
EnumValue
Enum(diagnostics_output_format) String(json) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON)
fdiagnostics-parseable-fixits fdiagnostics-parseable-fixits
Common Var(flag_diagnostics_parseable_fixits) Common Var(flag_diagnostics_parseable_fixits)
Print fix-it hints in machine-readable form. Print fix-it hints in machine-readable form.
......
/* JSON output for diagnostics
Copyright (C) 2018 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "diagnostic.h"
#include "json.h"
/* The top-level JSON array of pending diagnostics. */
static json::array *toplevel_array;
/* The JSON object for the current diagnostic group. */
static json::object *cur_group;
/* The JSON array for the "children" array within the current diagnostic
group. */
static json::array *cur_children_array;
/* Generate a JSON object for LOC. */
static json::object *
json_from_expanded_location (location_t loc)
{
expanded_location exploc = expand_location (loc);
json::object *result = new json::object ();
result->set ("file", new json::string (exploc.file));
result->set ("line", new json::number (exploc.line));
result->set ("column", new json::number (exploc.column));
return result;
}
/* Generate a JSON object for LOC_RANGE. */
static json::object *
json_from_location_range (const location_range *loc_range, unsigned range_idx)
{
location_t caret_loc = get_pure_location (loc_range->m_loc);
if (caret_loc == UNKNOWN_LOCATION)
return NULL;
location_t start_loc = get_start (loc_range->m_loc);
location_t finish_loc = get_finish (loc_range->m_loc);
json::object *result = new json::object ();
result->set ("caret", json_from_expanded_location (caret_loc));
if (start_loc != caret_loc)
result->set ("start", json_from_expanded_location (start_loc));
if (finish_loc != caret_loc)
result->set ("finish", json_from_expanded_location (finish_loc));
if (loc_range->m_label)
{
label_text text;
text = loc_range->m_label->get_text (range_idx);
if (text.m_buffer)
result->set ("label", new json::string (text.m_buffer));
text.maybe_free ();
}
return result;
}
/* Generate a JSON object for HINT. */
static json::object *
json_from_fixit_hint (const fixit_hint *hint)
{
json::object *fixit_obj = new json::object ();
location_t start_loc = hint->get_start_loc ();
fixit_obj->set ("start", json_from_expanded_location (start_loc));
location_t next_loc = hint->get_next_loc ();
fixit_obj->set ("next", json_from_expanded_location (next_loc));
fixit_obj->set ("string", new json::string (hint->get_string ()));
return fixit_obj;
}
/* No-op implementation of "begin_diagnostic" for JSON output. */
static void
json_begin_diagnostic (diagnostic_context *, diagnostic_info *)
{
}
/* Implementation of "end_diagnostic" for JSON output.
Generate a JSON object for DIAGNOSTIC, and store for output
within current diagnostic group. */
static void
json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
diagnostic_t orig_diag_kind)
{
json::object *diag_obj = new json::object ();
/* Get "kind" of diagnostic. */
{
static const char *const diagnostic_kind_text[] = {
#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
#include "diagnostic.def"
#undef DEFINE_DIAGNOSTIC_KIND
"must-not-happen"
};
/* Lose the trailing ": ". */
const char *kind_text = diagnostic_kind_text[diagnostic->kind];
size_t len = strlen (kind_text);
gcc_assert (len > 2);
gcc_assert (kind_text[len - 2] == ':');
gcc_assert (kind_text[len - 1] == ' ');
char *rstrip = xstrdup (kind_text);
rstrip[len - 2] = '\0';
diag_obj->set ("kind", new json::string (rstrip));
free (rstrip);
}
// FIXME: encoding of the message (json::string requires UTF-8)
diag_obj->set ("message",
new json::string (pp_formatted_text (context->printer)));
pp_clear_output_area (context->printer);
char *option_text;
option_text = context->option_name (context, diagnostic->option_index,
orig_diag_kind, diagnostic->kind);
if (option_text)
{
diag_obj->set ("option", new json::string (option_text));
free (option_text);
}
/* If we've already emitted a diagnostic within this auto_diagnostic_group,
then add diag_obj to its "children" array. */
if (cur_group)
{
gcc_assert (cur_children_array);
cur_children_array->append (diag_obj);
}
else
{
/* Otherwise, make diag_obj be the top-level object within the group;
add a "children" array. */
toplevel_array->append (diag_obj);
cur_group = diag_obj;
cur_children_array = new json::array ();
diag_obj->set ("children", cur_children_array);
}
const rich_location *richloc = diagnostic->richloc;
json::array *loc_array = new json::array ();
diag_obj->set ("locations", loc_array);
for (unsigned int i = 0; i < richloc->get_num_locations (); i++)
{
const location_range *loc_range = richloc->get_range (i);
json::object *loc_obj = json_from_location_range (loc_range, i);
if (loc_obj)
loc_array->append (loc_obj);
}
if (richloc->get_num_fixit_hints ())
{
json::array *fixit_array = new json::array ();
diag_obj->set ("fixits", fixit_array);
for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
{
const fixit_hint *hint = richloc->get_fixit_hint (i);
json::object *fixit_obj = json_from_fixit_hint (hint);
fixit_array->append (fixit_obj);
}
}
/* TODO: tree-ish things:
TODO: functions
TODO: inlining information
TODO: macro expansion information. */
}
/* No-op implementation of "begin_group_cb" for JSON output. */
static void
json_begin_group (diagnostic_context *)
{
}
/* Implementation of "end_group_cb" for JSON output. */
static void
json_end_group (diagnostic_context *)
{
cur_group = NULL;
cur_children_array = NULL;
}
/* Callback for final cleanup for JSON output. */
static void
json_final_cb (diagnostic_context *)
{
/* Flush the top-level array. */
toplevel_array->dump (stderr);
fprintf (stderr, "\n");
delete toplevel_array;
toplevel_array = NULL;
}
/* Set the output format for CONTEXT to FORMAT. */
void
diagnostic_output_format_init (diagnostic_context *context,
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:
{
/* 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;
/* 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;
}
}
...@@ -131,6 +131,28 @@ diagnostic_set_caret_max_width (diagnostic_context *context, int value) ...@@ -131,6 +131,28 @@ diagnostic_set_caret_max_width (diagnostic_context *context, int value)
context->caret_max_width = value; context->caret_max_width = value;
} }
/* Default implementation of final_cb. */
static void
default_diagnostic_final_cb (diagnostic_context *context)
{
/* Some of the errors may actually have been warnings. */
if (diagnostic_kind_count (context, DK_WERROR))
{
/* -Werror was given. */
if (context->warning_as_error_requested)
pp_verbatim (context->printer,
_("%s: all warnings being treated as errors"),
progname);
/* At least one -Werror= was given. */
else
pp_verbatim (context->printer,
_("%s: some warnings being treated as errors"),
progname);
pp_newline_and_flush (context->printer);
}
}
/* Initialize the diagnostic message outputting machinery. */ /* Initialize the diagnostic message outputting machinery. */
void void
diagnostic_initialize (diagnostic_context *context, int n_opts) diagnostic_initialize (diagnostic_context *context, int n_opts)
...@@ -185,6 +207,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) ...@@ -185,6 +207,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
context->diagnostic_group_emission_count = 0; context->diagnostic_group_emission_count = 0;
context->begin_group_cb = NULL; context->begin_group_cb = NULL;
context->end_group_cb = NULL; context->end_group_cb = NULL;
context->final_cb = default_diagnostic_final_cb;
} }
/* Maybe initialize the color support. We require clients to do this /* Maybe initialize the color support. We require clients to do this
...@@ -220,21 +243,8 @@ diagnostic_color_init (diagnostic_context *context, int value /*= -1 */) ...@@ -220,21 +243,8 @@ diagnostic_color_init (diagnostic_context *context, int value /*= -1 */)
void void
diagnostic_finish (diagnostic_context *context) diagnostic_finish (diagnostic_context *context)
{ {
/* Some of the errors may actually have been warnings. */ if (context->final_cb)
if (diagnostic_kind_count (context, DK_WERROR)) context->final_cb (context);
{
/* -Werror was given. */
if (context->warning_as_error_requested)
pp_verbatim (context->printer,
_("%s: all warnings being treated as errors"),
progname);
/* At least one -Werror= was given. */
else
pp_verbatim (context->printer,
_("%s: some warnings being treated as errors"),
progname);
pp_newline_and_flush (context->printer);
}
diagnostic_file_cache_fini (); diagnostic_file_cache_fini ();
...@@ -642,7 +652,8 @@ default_diagnostic_start_span_fn (diagnostic_context *context, ...@@ -642,7 +652,8 @@ default_diagnostic_start_span_fn (diagnostic_context *context,
void void
default_diagnostic_finalizer (diagnostic_context *context, default_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t)
{ {
diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind); diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
pp_destroy_prefix (context->printer); pp_destroy_prefix (context->printer);
...@@ -1006,7 +1017,7 @@ diagnostic_report_diagnostic (diagnostic_context *context, ...@@ -1006,7 +1017,7 @@ diagnostic_report_diagnostic (diagnostic_context *context,
pp_output_formatted_text (context->printer); pp_output_formatted_text (context->printer);
if (context->show_option_requested) if (context->show_option_requested)
print_option_information (context, diagnostic, orig_diag_kind); print_option_information (context, diagnostic, orig_diag_kind);
(*diagnostic_finalizer (context)) (context, diagnostic); (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);
if (context->parseable_fixits_p) if (context->parseable_fixits_p)
{ {
print_parseable_fixits (context->printer, diagnostic->richloc); print_parseable_fixits (context->printer, diagnostic->richloc);
......
...@@ -24,6 +24,17 @@ along with GCC; see the file COPYING3. If not see ...@@ -24,6 +24,17 @@ along with GCC; see the file COPYING3. If not see
#include "pretty-print.h" #include "pretty-print.h"
#include "diagnostic-core.h" #include "diagnostic-core.h"
/* Enum for overriding the standard output format. */
enum diagnostics_output_format
{
/* The default: textual output. */
DIAGNOSTICS_OUTPUT_FORMAT_TEXT,
/* JSON-based output. */
DIAGNOSTICS_OUTPUT_FORMAT_JSON
};
/* A diagnostic is described by the MESSAGE to send, the FILE and LINE of /* A diagnostic is described by the MESSAGE to send, the FILE and LINE of
its context and its KIND (ice, error, warning, note, ...) See complete its context and its KIND (ice, error, warning, note, ...) See complete
list in diagnostic.def. */ list in diagnostic.def. */
...@@ -60,7 +71,9 @@ typedef void (*diagnostic_starter_fn) (diagnostic_context *, ...@@ -60,7 +71,9 @@ typedef void (*diagnostic_starter_fn) (diagnostic_context *,
typedef void (*diagnostic_start_span_fn) (diagnostic_context *, typedef void (*diagnostic_start_span_fn) (diagnostic_context *,
expanded_location); expanded_location);
typedef diagnostic_starter_fn diagnostic_finalizer_fn; typedef void (*diagnostic_finalizer_fn) (diagnostic_context *,
diagnostic_info *,
diagnostic_t);
class edit_context; class edit_context;
...@@ -243,6 +256,9 @@ struct diagnostic_context ...@@ -243,6 +256,9 @@ struct diagnostic_context
/* If non-NULL, this will be called when a stack of groups is /* If non-NULL, this will be called when a stack of groups is
popped if any diagnostics were emitted within that group. */ popped if any diagnostics were emitted within that group. */
void (*end_group_cb) (diagnostic_context * context); void (*end_group_cb) (diagnostic_context * context);
/* Callback for final cleanup. */
void (*final_cb) (diagnostic_context *context);
}; };
static inline void static inline void
...@@ -341,7 +357,8 @@ extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_inf ...@@ -341,7 +357,8 @@ extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_inf
void default_diagnostic_starter (diagnostic_context *, diagnostic_info *); void default_diagnostic_starter (diagnostic_context *, diagnostic_info *);
void default_diagnostic_start_span_fn (diagnostic_context *, void default_diagnostic_start_span_fn (diagnostic_context *,
expanded_location); expanded_location);
void default_diagnostic_finalizer (diagnostic_context *, diagnostic_info *); void default_diagnostic_finalizer (diagnostic_context *, diagnostic_info *,
diagnostic_t);
void diagnostic_set_caret_max_width (diagnostic_context *context, int value); void diagnostic_set_caret_max_width (diagnostic_context *context, int value);
void diagnostic_action_after_output (diagnostic_context *, diagnostic_t); void diagnostic_action_after_output (diagnostic_context *, diagnostic_t);
void diagnostic_check_max_errors (diagnostic_context *, bool flush = false); void diagnostic_check_max_errors (diagnostic_context *, bool flush = false);
...@@ -401,5 +418,7 @@ extern char *file_name_as_prefix (diagnostic_context *, const char *); ...@@ -401,5 +418,7 @@ extern char *file_name_as_prefix (diagnostic_context *, const char *);
extern char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1; extern char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1;
extern void diagnostic_output_format_init (diagnostic_context *,
enum diagnostics_output_format);
#endif /* ! GCC_DIAGNOSTIC_H */ #endif /* ! GCC_DIAGNOSTIC_H */
...@@ -268,6 +268,7 @@ Objective-C and Objective-C++ Dialects}. ...@@ -268,6 +268,7 @@ Objective-C and Objective-C++ Dialects}.
@gccoptlist{-fmessage-length=@var{n} @gol @gccoptlist{-fmessage-length=@var{n} @gol
-fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]} @gol -fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]} @gol
-fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]} @gol -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]} @gol
-fdiagnostics-format=@r{[}text@r{|}json@r{]} @gol
-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol -fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol -fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol
-fdiagnostics-minimum-margin-width=@var{width} @gol -fdiagnostics-minimum-margin-width=@var{width} @gol
...@@ -3979,6 +3980,193 @@ Do not print column numbers in diagnostics. This may be necessary if ...@@ -3979,6 +3980,193 @@ Do not print column numbers in diagnostics. This may be necessary if
diagnostics are being scanned by a program that does not understand the diagnostics are being scanned by a program that does not understand the
column numbers, such as @command{dejagnu}. column numbers, such as @command{dejagnu}.
@item -fdiagnostics-format=@var{FORMAT}
@opindex fdiagnostics-format
Select a different format for printing diagnostics.
@var{FORMAT} is @samp{text} or @samp{json}.
The default is @samp{text}.
The @samp{json} format 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:
@smallexample
misleading-indentation.c:15:3: warning: this 'if' clause does not
guard... [-Wmisleading-indentation]
15 | if (flag)
| ^~
misleading-indentation.c:17:5: note: ...this statement, but the latter
is misleadingly indented as if it were guarded by the 'if'
17 | y = 2;
| ^
@end smallexample
@noindent
might be printed in JSON form (after formatting) like this:
@smallexample
[
@{
"kind": "warning",
"locations": [
@{
"caret": @{
"column": 3,
"file": "misleading-indentation.c",
"line": 15
@},
"finish": @{
"column": 4,
"file": "misleading-indentation.c",
"line": 15
@}
@}
],
"message": "this \u2018if\u2019 clause does not guard...",
"option": "-Wmisleading-indentation",
"children": [
@{
"kind": "note",
"locations": [
@{
"caret": @{
"column": 5,
"file": "misleading-indentation.c",
"line": 17
@}
@}
],
"message": "...this statement, but the latter is @dots{}"
@}
]
@},
@dots{}
]
@end smallexample
@noindent
where the @code{note} is a child of the @code{warning}.
A diagnostic has a @code{kind}. If this is @code{warning}, then there is
an @code{option} key describing the command-line option controlling the
warning.
A diagnostic can contain zero or more locations. Each location has up
to three positions within it: a @code{caret} position and optional
@code{start} and @code{finish} positions. A location can also have
an optional @code{label} string. For example, this error:
@smallexample
bad-binary-ops.c:64:23: error: invalid operands to binary + (have 'S' @{aka
'struct s'@} and 'T' @{aka 'struct t'@})
64 | return callee_4a () + callee_4b ();
| ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
| | |
| | T @{aka struct t@}
| S @{aka struct s@}
@end smallexample
@noindent
has three locations. Its primary location is at the ``+'' token at column
23. It has two secondary locations, describing the left and right-hand sides
of the expression, which have labels. It might be printed in JSON form as:
@smallexample
@{
"children": [],
"kind": "error",
"locations": [
@{
"caret": @{
"column": 23, "file": "bad-binary-ops.c", "line": 64
@}
@},
@{
"caret": @{
"column": 10, "file": "bad-binary-ops.c", "line": 64
@},
"finish": @{
"column": 21, "file": "bad-binary-ops.c", "line": 64
@},
"label": "S @{aka struct s@}"
@},
@{
"caret": @{
"column": 25, "file": "bad-binary-ops.c", "line": 64
@},
"finish": @{
"column": 36, "file": "bad-binary-ops.c", "line": 64
@},
"label": "T @{aka struct t@}"
@}
],
"message": "invalid operands to binary + @dots{}"
@}
@end smallexample
If a diagnostic contains fix-it hints, it has a @code{fixits} array,
consisting of half-open intervals, similar to the output of
@option{-fdiagnostics-parseable-fixits}. For example, this diagnostic
with a replacement fix-it hint:
@smallexample
demo.c:8:15: error: 'struct s' has no member named 'colour'; did you
mean 'color'?
8 | return ptr->colour;
| ^~~~~~
| color
@end smallexample
@noindent
might be printed in JSON form as:
@smallexample
@{
"children": [],
"fixits": [
@{
"next": @{
"column": 21,
"file": "demo.c",
"line": 8
@},
"start": @{
"column": 15,
"file": "demo.c",
"line": 8
@},
"string": "color"
@}
],
"kind": "error",
"locations": [
@{
"caret": @{
"column": 15,
"file": "demo.c",
"line": 8
@},
"finish": @{
"column": 20,
"file": "demo.c",
"line": 8
@}
@}
],
"message": "\u2018struct s\u2019 has no member named @dots{}"
@}
@end smallexample
@noindent
where the fix-it hint suggests replacing the text from @code{start} up
to but not including @code{next} with @code{string}'s value. Deletions
are expressed via an empty value for @code{string}, insertions by
having @code{start} equal @code{next}.
@end table @end table
@node Warning Options @node Warning Options
...@@ -24267,6 +24267,7 @@ gen_producer_string (void) ...@@ -24267,6 +24267,7 @@ gen_producer_string (void)
case OPT_fdiagnostics_show_labels: case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_show_line_numbers:
case OPT_fdiagnostics_color_: case OPT_fdiagnostics_color_:
case OPT_fdiagnostics_format_:
case OPT_fverbose_asm: case OPT_fverbose_asm:
case OPT____: case OPT____:
case OPT__sysroot_: case OPT__sysroot_:
......
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
* error.c (gfc_diagnostic_finalizer): Add diagnostic_t param.
2018-11-13 David Malcolm <dmalcolm@redhat.com> 2018-11-13 David Malcolm <dmalcolm@redhat.com>
* cpp.c: Replace "source_location" with "location_t". * cpp.c: Replace "source_location" with "location_t".
......
...@@ -1132,7 +1132,8 @@ gfc_diagnostic_start_span (diagnostic_context *context, ...@@ -1132,7 +1132,8 @@ gfc_diagnostic_start_span (diagnostic_context *context,
static void static void
gfc_diagnostic_finalizer (diagnostic_context *context, gfc_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic ATTRIBUTE_UNUSED) diagnostic_info *diagnostic ATTRIBUTE_UNUSED,
diagnostic_t orig_diag_kind ATTRIBUTE_UNUSED)
{ {
pp_destroy_prefix (context->printer); pp_destroy_prefix (context->printer);
pp_newline_and_flush (context->printer); pp_newline_and_flush (context->printer);
......
...@@ -4001,6 +4001,11 @@ driver_handle_option (struct gcc_options *opts, ...@@ -4001,6 +4001,11 @@ driver_handle_option (struct gcc_options *opts,
diagnostic_color_init (dc, value); diagnostic_color_init (dc, value);
break; break;
case OPT_fdiagnostics_format_:
diagnostic_output_format_init (dc,
(enum diagnostics_output_format)value);
break;
case OPT_Wa_: case OPT_Wa_:
{ {
int prev, j; int prev, j;
......
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
* dummy-frontend.c (jit_begin_diagnostic): Add diagnostic_t param.
2018-11-13 David Malcolm <dmalcolm@redhat.com> 2018-11-13 David Malcolm <dmalcolm@redhat.com>
* jit-playback.c: Replace "source_location" with "location_t". * jit-playback.c: Replace "source_location" with "location_t".
......
...@@ -110,7 +110,8 @@ jit_begin_diagnostic (diagnostic_context */*context*/, ...@@ -110,7 +110,8 @@ jit_begin_diagnostic (diagnostic_context */*context*/,
static void static void
jit_end_diagnostic (diagnostic_context *context, jit_end_diagnostic (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t)
{ {
gcc_assert (gcc::jit::active_playback_ctxt); gcc_assert (gcc::jit::active_playback_ctxt);
JIT_LOG_SCOPE (gcc::jit::active_playback_ctxt->get_logger ()); JIT_LOG_SCOPE (gcc::jit::active_playback_ctxt->get_logger ());
......
...@@ -646,6 +646,7 @@ append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts, ...@@ -646,6 +646,7 @@ append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts,
switch (option->opt_index) switch (option->opt_index)
{ {
case OPT_fdiagnostics_color_: case OPT_fdiagnostics_color_:
case OPT_fdiagnostics_format_:
case OPT_fdiagnostics_show_caret: case OPT_fdiagnostics_show_caret:
case OPT_fdiagnostics_show_labels: case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_show_line_numbers:
......
...@@ -2232,6 +2232,11 @@ common_handle_option (struct gcc_options *opts, ...@@ -2232,6 +2232,11 @@ common_handle_option (struct gcc_options *opts,
diagnostic_color_init (dc, value); diagnostic_color_init (dc, value);
break; break;
case OPT_fdiagnostics_format_:
diagnostic_output_format_init (dc,
(enum diagnostics_output_format)value);
break;
case OPT_fdiagnostics_parseable_fixits: case OPT_fdiagnostics_parseable_fixits:
dc->parseable_fixits_p = value; dc->parseable_fixits_p = value;
break; break;
......
2018-11-15 David Malcolm <dmalcolm@redhat.com>
PR other/19165
* c-c++-common/diagnostic-format-json-1.c: New test.
* c-c++-common/diagnostic-format-json-2.c: New test.
* c-c++-common/diagnostic-format-json-3.c: New test.
* c-c++-common/diagnostic-format-json-4.c: New test.
* c-c++-common/diagnostic-format-json-5.c: New test.
* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
(custom_diagnostic_finalizer): Add diagnostic_t param.
* gcc.dg/plugin/location_overflow_plugin.c
(verify_unpacked_ranges): Likewise.
(verify_no_columns): Likewise.
* gfortran.dg/diagnostic-format-json-1.F90: New test.
* gfortran.dg/diagnostic-format-json-2.F90: New test.
* gfortran.dg/diagnostic-format-json-3.F90: New test.
2018-11-15 Richard Biener <rguenther@suse.de> 2018-11-15 Richard Biener <rguenther@suse.de>
PR middle-end/88029 PR middle-end/88029
......
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json" } */
#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 "\"message\": \"#error message\"" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 2" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 6" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
/* { dg-regexp "\[\[\{\}, \]*\]" } */
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json" } */
#warning 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\": \"warning\"" } */
/* { dg-regexp "\"message\": \"#warning message\"" } */
/* { dg-regexp "\"option\": \"-Wcpp\"" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 2" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 8" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
/* { dg-regexp "\[\[\{\}, \]*\]" } */
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json -Werror" } */
#warning 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 "\"message\": \"#warning message\"" } */
/* { dg-regexp "\"option\": \"-Werror=cpp\"" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 2" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.c\"" } */
/* { dg-regexp "\"line\": 4" } */
/* { dg-regexp "\"column\": 8" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
/* { dg-regexp "\[\[\{\}, \]*\]" } */
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json -Wmisleading-indentation" } */
int test (void)
{
if (1)
return 3;
return 4;
return 5;
}
/* 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. */
/* Verify nested diagnostics. */
/* The nested diagnostic. */
/* { dg-regexp "\"kind\": \"note\"" } */
/* { dg-regexp "\"message\": \"...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'\"" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 5" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 10" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* The outer diagnostic. */
/* { dg-regexp "\"kind\": \"warning\"" } */
/* { dg-regexp "\"message\": \"this 'if' clause does not guard...\"" } */
/* { dg-regexp "\"option\": \"-Wmisleading-indentation\"" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
/* { dg-regexp "\"line\": 6" } */
/* { dg-regexp "\"column\": 3" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
/* { dg-regexp "\"line\": 6" } */
/* { dg-regexp "\"column\": 4" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"children\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\[\[\{\}, \]*\]" } */
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json" } */
struct s { int color; };
int test (struct s *ptr)
{
return ptr->colour;
}
/* 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 "\"message\": \".*\"" } */
/* Verify fix-it hints. */
/* { dg-regexp "\"string\": \"color\"" } */
/* { dg-regexp "\"start\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 15" } */
/* { dg-regexp "\"next\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 21" } */
/* { dg-regexp "\"fixits\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"caret\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 15" } */
/* { dg-regexp "\"finish\": \{" } */
/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-5.c\"" } */
/* { dg-regexp "\"line\": 8" } */
/* { dg-regexp "\"column\": 20" } */
/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
/* { dg-regexp "\[\[\{\}, \]*\]" } */
...@@ -129,7 +129,8 @@ static bool force_show_locus_color = false; ...@@ -129,7 +129,8 @@ static bool force_show_locus_color = false;
static void static void
custom_diagnostic_finalizer (diagnostic_context *context, custom_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t)
{ {
bool old_show_color = pp_show_color (context->printer); bool old_show_color = pp_show_color (context->printer);
if (force_show_locus_color) if (force_show_locus_color)
......
...@@ -39,7 +39,8 @@ static diagnostic_finalizer_fn original_finalizer = NULL; ...@@ -39,7 +39,8 @@ static diagnostic_finalizer_fn original_finalizer = NULL;
static void static void
verify_unpacked_ranges (diagnostic_context *context, verify_unpacked_ranges (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t orig_diag_kind)
{ {
/* Verify that the locations are ad-hoc, not packed. */ /* Verify that the locations are ad-hoc, not packed. */
location_t loc = diagnostic_location (diagnostic); location_t loc = diagnostic_location (diagnostic);
...@@ -47,12 +48,13 @@ verify_unpacked_ranges (diagnostic_context *context, ...@@ -47,12 +48,13 @@ verify_unpacked_ranges (diagnostic_context *context,
/* We're done testing; chain up to original finalizer. */ /* We're done testing; chain up to original finalizer. */
gcc_assert (original_finalizer); gcc_assert (original_finalizer);
original_finalizer (context, diagnostic); original_finalizer (context, diagnostic, orig_diag_kind);
} }
static void static void
verify_no_columns (diagnostic_context *context, verify_no_columns (diagnostic_context *context,
diagnostic_info *diagnostic) diagnostic_info *diagnostic,
diagnostic_t orig_diag_kind)
{ {
/* Verify that the locations have no columns. */ /* Verify that the locations have no columns. */
location_t loc = diagnostic_location (diagnostic); location_t loc = diagnostic_location (diagnostic);
...@@ -60,7 +62,7 @@ verify_no_columns (diagnostic_context *context, ...@@ -60,7 +62,7 @@ verify_no_columns (diagnostic_context *context,
/* We're done testing; chain up to original finalizer. */ /* We're done testing; chain up to original finalizer. */
gcc_assert (original_finalizer); gcc_assert (original_finalizer);
original_finalizer (context, diagnostic); original_finalizer (context, diagnostic, orig_diag_kind);
} }
int int
......
! { dg-do compile }
! { dg-options "-fdiagnostics-format=json" }
#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 "\"message\": \"#error message\"" }
! { dg-regexp "\"caret\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 2" }
! { dg-regexp "\"finish\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 6" }
! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
! { dg-regexp "\"children\": \[\[\]\[\]\]" }
! { dg-regexp "\[\[\{\}, \]*\]" }
! { dg-do compile }
! { dg-options "-fdiagnostics-format=json" }
#warning 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\": \"warning\"" }
! { dg-regexp "\"message\": \"#warning message\"" }
! { dg-regexp "\"option\": \"-Wcpp\"" }
! { dg-regexp "\"caret\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 2" }
! { dg-regexp "\"finish\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 8" }
! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
! { dg-regexp "\"children\": \[\[\]\[\]\]" }
! { dg-regexp "\[\[\{\}, \]*\]" }
! { dg-do compile }
! { dg-options "-fdiagnostics-format=json -Werror" }
#warning 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 "\"message\": \"#warning message\"" }
! { dg-regexp "\"option\": \"-Werror=cpp\"" }
! { dg-regexp "\"caret\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 2" }
! { dg-regexp "\"finish\": \{" }
! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.F90\"" }
! { dg-regexp "\"line\": 4" }
! { dg-regexp "\"column\": 8" }
! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
! { dg-regexp "\"children\": \[\[\]\[\]\]" }
! { dg-regexp "\[\[\{\}, \]*\]" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment