mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 06:49:09 +02:00
Add json-diagnostic.{cc,h}
This patch adds support for emitting diagnostics about JSON input files to global_dc, showing both the file/line/columns and the JSON Pointer for the problematic json::value. Test coverage is added by the followup on aarch64. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add json-diagnostic.o. * diagnostics/client-data-hooks.h (class client_data_hooks_decorator): New. * diagnostics/context.cc (context::set_client_data_hooks): Return the old hooks. * diagnostics/context.h (context::set_client_data_hooks): Update decl likewise. * json-diagnostic.cc: New file. * json-diagnostic.h: New file. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
@@ -1905,7 +1905,7 @@ OBJS-libcommon = \
|
||||
gcc-diagnostic-spec.o \
|
||||
graphviz.o pex.o \
|
||||
pretty-print.o intl.o \
|
||||
json.o json-parsing.o \
|
||||
json.o json-parsing.o json-diagnostic.o \
|
||||
pub-sub.o \
|
||||
xml.o \
|
||||
sbitmap.o \
|
||||
|
||||
@@ -63,6 +63,59 @@ class client_data_hooks
|
||||
add_sarif_invocation_properties (sarif_object &invocation_obj) const = 0;
|
||||
};
|
||||
|
||||
/* Implementation of client_data_hooks that delegates vfuncs to an
|
||||
optional inner object. */
|
||||
|
||||
class client_data_hooks_decorator : public client_data_hooks
|
||||
{
|
||||
public:
|
||||
client_data_hooks_decorator (const client_data_hooks *inner)
|
||||
: m_inner (inner)
|
||||
{
|
||||
}
|
||||
|
||||
const client_version_info *get_any_version_info () const override
|
||||
{
|
||||
if (m_inner)
|
||||
return m_inner->get_any_version_info ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const logical_locations::manager *
|
||||
get_logical_location_manager () const override
|
||||
{
|
||||
if (m_inner)
|
||||
return m_inner->get_logical_location_manager ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
logical_locations::key
|
||||
get_current_logical_location () const override
|
||||
{
|
||||
if (m_inner)
|
||||
return m_inner->get_current_logical_location ();
|
||||
return logical_locations::key ();
|
||||
}
|
||||
|
||||
const char *
|
||||
maybe_get_sarif_source_language (const char *filename) const override
|
||||
{
|
||||
if (m_inner)
|
||||
return m_inner->maybe_get_sarif_source_language (filename);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
add_sarif_invocation_properties (sarif_object &invocation_obj) const override
|
||||
{
|
||||
if (m_inner)
|
||||
m_inner->add_sarif_invocation_properties (invocation_obj);
|
||||
}
|
||||
|
||||
private:
|
||||
const client_data_hooks *m_inner;
|
||||
};
|
||||
|
||||
class client_plugin_info;
|
||||
|
||||
/* Abstract base class for a diagnostics::context to get at
|
||||
|
||||
@@ -529,12 +529,13 @@ context::set_main_input_filename (const char *filename)
|
||||
sink_->set_main_input_filename (filename);
|
||||
}
|
||||
|
||||
void
|
||||
std::unique_ptr<client_data_hooks>
|
||||
context::set_client_data_hooks (std::unique_ptr<client_data_hooks> hooks)
|
||||
{
|
||||
delete m_client_data_hooks;
|
||||
std::unique_ptr<client_data_hooks> old_hooks (m_client_data_hooks);
|
||||
/* Ideally the field would be a std::unique_ptr here. */
|
||||
m_client_data_hooks = hooks.release ();
|
||||
return old_hooks;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -388,7 +388,9 @@ public:
|
||||
/* Various setters for use by option-handling logic. */
|
||||
void set_sink (std::unique_ptr<sink> sink_);
|
||||
void set_text_art_charset (enum diagnostic_text_art_charset charset);
|
||||
void set_client_data_hooks (std::unique_ptr<client_data_hooks> hooks);
|
||||
|
||||
std::unique_ptr<client_data_hooks>
|
||||
set_client_data_hooks (std::unique_ptr<client_data_hooks> hooks);
|
||||
|
||||
void push_owned_urlifier (std::unique_ptr<urlifier>);
|
||||
void push_borrowed_urlifier (const urlifier &);
|
||||
|
||||
374
gcc/json-diagnostic.cc
Normal file
374
gcc/json-diagnostic.cc
Normal file
@@ -0,0 +1,374 @@
|
||||
/* Diagnostics relating to JSON values.
|
||||
Copyright (C) 2026 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/>. */
|
||||
|
||||
#define INCLUDE_MAP
|
||||
#define INCLUDE_STRING
|
||||
#define INCLUDE_VECTOR
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "intl.h"
|
||||
#include "diagnostic.h"
|
||||
#include "json-diagnostic.h"
|
||||
#include "diagnostics/dumping.h"
|
||||
#include "diagnostics/logging.h"
|
||||
#include "diagnostics/logical-locations.h"
|
||||
#include "diagnostics/client-data-hooks.h"
|
||||
#include "diagnostics/text-sink.h"
|
||||
#include "diagnostics/physical-location-maker.h"
|
||||
#include "pretty-print-markup.h"
|
||||
|
||||
static bool
|
||||
emit_json_diagnostic (gcc_json_context &ctxt,
|
||||
enum diagnostics::kind kind,
|
||||
const json::value &js_val,
|
||||
diagnostics::option_id option_id,
|
||||
const char *gmsgid, va_list *ap)
|
||||
ATTRIBUTE_GCC_DIAG(5,0);
|
||||
|
||||
using log_function_params = diagnostics::logging::log_function_params;
|
||||
using auto_inc_log_depth = diagnostics::logging::auto_inc_depth;
|
||||
|
||||
class json_logical_location_manager
|
||||
: public diagnostics::logical_locations::manager
|
||||
{
|
||||
public:
|
||||
using key = diagnostics::logical_locations::key;
|
||||
using kind = diagnostics::logical_locations::kind;
|
||||
|
||||
void
|
||||
dump (FILE *outfile, int indent) const final override
|
||||
{
|
||||
diagnostics::dumping::emit_heading (outfile, indent,
|
||||
"json_logical_location_manager");
|
||||
}
|
||||
|
||||
label_text
|
||||
get_short_name (key k) const final override
|
||||
{
|
||||
auto *js_val = js_from_key (k);
|
||||
const json::pointer::token &pointer_token = js_val->get_pointer_token ();
|
||||
pretty_printer pp;
|
||||
pointer_token.print (&pp);
|
||||
return label_text::take (xstrdup (pp_formatted_text (&pp)));
|
||||
}
|
||||
|
||||
label_text
|
||||
get_name_with_scope (key k) const final override
|
||||
{
|
||||
auto *js_val = js_from_key (k);
|
||||
pretty_printer pp;
|
||||
js_val->print_pointer (&pp);
|
||||
return label_text::take (xstrdup (pp_formatted_text (&pp)));
|
||||
}
|
||||
|
||||
label_text
|
||||
get_internal_name (key) const final override
|
||||
{
|
||||
return label_text ();
|
||||
}
|
||||
|
||||
kind
|
||||
get_kind (key k) const final override
|
||||
{
|
||||
auto *js_val = js_from_key (k);
|
||||
|
||||
switch (js_val->get_kind ())
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
|
||||
case json::JSON_OBJECT:
|
||||
return kind::object;
|
||||
|
||||
case json::JSON_ARRAY:
|
||||
return kind::array;
|
||||
|
||||
case json::JSON_INTEGER:
|
||||
case json::JSON_FLOAT:
|
||||
case json::JSON_STRING:
|
||||
case json::JSON_TRUE:
|
||||
case json::JSON_FALSE:
|
||||
case json::JSON_NULL:
|
||||
return kind::property;
|
||||
}
|
||||
}
|
||||
|
||||
label_text
|
||||
get_name_for_path_output (key k) const final override
|
||||
{
|
||||
return get_name_with_scope (k);
|
||||
}
|
||||
|
||||
key
|
||||
get_parent (key k) const final override
|
||||
{
|
||||
auto *js_val = js_from_key (k);
|
||||
auto &pointer_token = js_val->get_pointer_token ();
|
||||
return key_from_js (pointer_token.m_parent);
|
||||
}
|
||||
|
||||
static const json::value *
|
||||
js_from_key (key k)
|
||||
{
|
||||
return k.cast_to<const json::value *> ();
|
||||
}
|
||||
|
||||
static key
|
||||
key_from_js (const json::value *js_val)
|
||||
{
|
||||
return key::from_ptr (js_val);
|
||||
}
|
||||
|
||||
private:
|
||||
static void
|
||||
print_json_pointer_token (pretty_printer *);
|
||||
};
|
||||
|
||||
/* Implementation of diagnostics::client_data_hooks
|
||||
for reporting a diagnostic at a particular json::value
|
||||
in a JSON input file.
|
||||
|
||||
It wraps another hooks instance, but uses a
|
||||
json_logical_location_manager, has a specific
|
||||
json::value for the current logical location,
|
||||
and treats the SARIF source lang as "json". */
|
||||
|
||||
class json_client_data_hooks : public diagnostics::client_data_hooks_decorator
|
||||
{
|
||||
public:
|
||||
json_client_data_hooks (const json::value &js_val,
|
||||
const client_data_hooks *inner)
|
||||
: diagnostics::client_data_hooks_decorator (inner),
|
||||
m_js_val (js_val)
|
||||
{}
|
||||
|
||||
const diagnostics::logical_locations::manager *
|
||||
get_logical_location_manager () const override
|
||||
{
|
||||
return &m_logical_loc_mgr;
|
||||
}
|
||||
|
||||
diagnostics::logical_locations::key
|
||||
get_current_logical_location () const override
|
||||
{
|
||||
/* Use the json value's pointer as the key. */
|
||||
return json_logical_location_manager::key_from_js (&m_js_val);
|
||||
}
|
||||
|
||||
const char *
|
||||
maybe_get_sarif_source_language (const char *) const override
|
||||
{
|
||||
return "json";
|
||||
}
|
||||
|
||||
private:
|
||||
json_logical_location_manager m_logical_loc_mgr;
|
||||
const json::value &m_js_val;
|
||||
};
|
||||
|
||||
namespace pp_markup {
|
||||
|
||||
/* Print the JSON Pointer of a given json::value in quotes. */
|
||||
|
||||
class quoted_json_pointer : public pp_element
|
||||
{
|
||||
public:
|
||||
quoted_json_pointer (const json::value &js_val)
|
||||
: m_js_val (js_val)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
add_to_phase_2 (context &ctxt) final override
|
||||
{
|
||||
ctxt.begin_quote ();
|
||||
m_js_val.print_pointer (&ctxt.m_pp);
|
||||
ctxt.end_quote ();
|
||||
}
|
||||
|
||||
private:
|
||||
const json::value &m_js_val;
|
||||
};
|
||||
|
||||
} // namespace pp_markup
|
||||
|
||||
/* text_sink starter for diagnostics relating to JSON. */
|
||||
|
||||
static void
|
||||
json_text_starter (diagnostics::text_sink &sink,
|
||||
const diagnostics::diagnostic_info *diagnostic)
|
||||
{
|
||||
pretty_printer *pp = sink.get_printer ();
|
||||
|
||||
/* If this isn't the root value, report its json pointer. */
|
||||
diagnostics::logical_locations::key k;
|
||||
if (auto data_hooks = sink.get_context ().get_client_data_hooks ())
|
||||
k = data_hooks->get_current_logical_location ();
|
||||
const json::value *js_val = json_logical_location_manager::js_from_key (k);
|
||||
if (js_val && js_val->get_pointer_token ().m_parent)
|
||||
{
|
||||
const char *file = LOCATION_FILE (diagnostic_location (diagnostic));
|
||||
char *new_prefix = file ? sink.file_name_as_prefix (file) : nullptr;
|
||||
pp_set_prefix (pp, new_prefix);
|
||||
|
||||
pp_markup::quoted_json_pointer e (*js_val);
|
||||
switch (js_val->get_kind ())
|
||||
{
|
||||
default:
|
||||
pp_printf (pp, _("In JSON value %e"), &e);
|
||||
break;
|
||||
case json::JSON_OBJECT:
|
||||
pp_printf (pp, _("In JSON object %e"), &e);
|
||||
break;
|
||||
case json::JSON_ARRAY:
|
||||
pp_printf (pp, _("In JSON array %e"), &e);
|
||||
break;
|
||||
}
|
||||
pp_newline (pp);
|
||||
}
|
||||
|
||||
pp_set_prefix (pp, sink.build_prefix (*diagnostic));
|
||||
}
|
||||
|
||||
|
||||
/* class gcc_json_context : public json::simple_location_map. */
|
||||
|
||||
location_t
|
||||
gcc_json_context::make_location_for_point (const json::location_map::point &p)
|
||||
{
|
||||
diagnostics::physical_location_maker m (line_table);
|
||||
return m.new_location_from_file_line_column (m_filename,
|
||||
p.m_line,
|
||||
p.m_column + 1);
|
||||
}
|
||||
|
||||
location_t
|
||||
gcc_json_context::make_location_for_range (const json::location_map::range &r)
|
||||
{
|
||||
location_t start_loc = make_location_for_point (r.m_start);
|
||||
location_t end_loc = make_location_for_point (r.m_end);
|
||||
return make_location (start_loc, start_loc, end_loc);
|
||||
}
|
||||
|
||||
/* Emit a diagnostic on global_dc of the relevant KIND relating to JS_VAL,
|
||||
using CTXT to get at file/line/column info.
|
||||
|
||||
Temporarily override global_dc's logical location to refer to JS_VAL
|
||||
and the text_sink starter and finalizer to be suitable for handling
|
||||
reporting within a JSON file. */
|
||||
|
||||
static bool
|
||||
emit_json_diagnostic (gcc_json_context &ctxt,
|
||||
enum diagnostics::kind kind,
|
||||
const json::value &js_val,
|
||||
diagnostics::option_id option_id,
|
||||
const char *gmsgid, va_list *ap)
|
||||
{
|
||||
auto logger = global_dc->get_logger ();
|
||||
log_function_params (logger, __func__)
|
||||
.log_param_option_id ("option_id", option_id)
|
||||
.log_param_string ("gmsgid", gmsgid);
|
||||
auto_inc_log_depth depth_sentinel (logger);
|
||||
|
||||
auto_diagnostic_group d;
|
||||
|
||||
// Get physical location for JS_VAL.
|
||||
location_t phys_loc
|
||||
= ctxt.make_location_for_range (ctxt.get_range_for_value (js_val));
|
||||
rich_location richloc (line_table, phys_loc);
|
||||
|
||||
// Set logical location by overriding client data hooks
|
||||
auto tmp_client_data_hooks
|
||||
= std::make_unique<json_client_data_hooks>
|
||||
(js_val, global_dc->get_client_data_hooks ());
|
||||
auto old_client_data_hooks
|
||||
= global_dc->set_client_data_hooks (std::move (tmp_client_data_hooks));
|
||||
|
||||
// Override text hooks
|
||||
auto old_text_starter = text_starter (global_dc);
|
||||
auto old_text_finalizer = text_finalizer (global_dc);
|
||||
text_starter (global_dc) = json_text_starter;
|
||||
text_finalizer (global_dc) = diagnostics::default_text_finalizer;
|
||||
|
||||
bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_id,
|
||||
gmsgid, ap, kind);
|
||||
|
||||
// Restore old text and client data hooks:
|
||||
text_starter (global_dc) = old_text_starter;
|
||||
text_finalizer (global_dc) = old_text_finalizer;
|
||||
global_dc->set_client_data_hooks (std::move (old_client_data_hooks));
|
||||
|
||||
if (logger)
|
||||
logger->log_bool_return ("emit_diagnostic", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Emit an error on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
void
|
||||
json_error (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
const char *gmsgid, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
emit_json_diagnostic (ctxt,
|
||||
diagnostics::kind::error,
|
||||
js_val, -1,
|
||||
gmsgid, &ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
/* Emit a warning on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
bool
|
||||
json_warning (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
diagnostics::option_id option_id,
|
||||
const char *gmsgid, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
bool ret = emit_json_diagnostic (ctxt,
|
||||
diagnostics::kind::warning,
|
||||
js_val, option_id,
|
||||
gmsgid, &ap);
|
||||
va_end (ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Emit a note on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
void
|
||||
json_note (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
const char *gmsgid, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
emit_json_diagnostic (ctxt,
|
||||
diagnostics::kind::note,
|
||||
js_val, -1,
|
||||
gmsgid, &ap);
|
||||
va_end (ap);
|
||||
}
|
||||
74
gcc/json-diagnostic.h
Normal file
74
gcc/json-diagnostic.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Diagnostics relating to JSON values.
|
||||
Copyright (C) 2026 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_JSON_DIAGNOSTIC_H
|
||||
#define GCC_JSON_DIAGNOSTIC_H
|
||||
|
||||
#include "json-parsing.h"
|
||||
|
||||
/* Implementation of json::location_map for use with
|
||||
GCC diagnostics.
|
||||
Stores location information for json::value * from parsing, and
|
||||
can generate location_t values for the. */
|
||||
|
||||
class gcc_json_context : public json::simple_location_map
|
||||
{
|
||||
public:
|
||||
gcc_json_context (const char *filename)
|
||||
: m_filename (filename)
|
||||
{
|
||||
}
|
||||
|
||||
location_t
|
||||
make_location_for_point (const json::location_map::point &);
|
||||
|
||||
location_t
|
||||
make_location_for_range (const json::location_map::range &);
|
||||
|
||||
private:
|
||||
const char *m_filename;
|
||||
};
|
||||
|
||||
/* Emit an error on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
extern void
|
||||
json_error (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
const char *gmsgid, ...)
|
||||
ATTRIBUTE_GCC_DIAG(3,4);
|
||||
|
||||
/* Emit a warning on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
extern bool
|
||||
json_warning (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
diagnostics::option_id option_id,
|
||||
const char *gmsgid, ...)
|
||||
ATTRIBUTE_GCC_DIAG(4,5);
|
||||
|
||||
/* Emit a note on gcc's global_dc relating to JS_VAL. */
|
||||
|
||||
extern void
|
||||
json_note (gcc_json_context &ctxt,
|
||||
const json::value &js_val,
|
||||
const char *gmsgid, ...)
|
||||
ATTRIBUTE_GCC_DIAG(3,4);
|
||||
|
||||
#endif /* GCC_JSON_DIAGNOSTIC_H */
|
||||
Reference in New Issue
Block a user