diff options
author | David Malcolm <dmalcolm@redhat.com> | 2025-05-06 09:26:19 -0400 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2025-05-06 09:26:19 -0400 |
commit | 52fe9502eb153f87154cfcb6a58ab84164d7e1d9 (patch) | |
tree | 4598ea8ba7c27f0ca3c060b3d23615091aab8c6e | |
parent | 9fb44cc4823106729d086713dd022a4590703a4c (diff) | |
download | gcc-52fe9502eb153f87154cfcb6a58ab84164d7e1d9.zip gcc-52fe9502eb153f87154cfcb6a58ab84164d7e1d9.tar.gz gcc-52fe9502eb153f87154cfcb6a58ab84164d7e1d9.tar.bz2 |
json: implement JSON pointer; use it in sarif-replay [PR117988]
This patch extends our json class to track JSON pointers (RFC 6901),
and then uses this within sarif-replay to provide logical locations
within the JSON when reporting on issues in the SARIF.
gcc/ChangeLog:
PR sarif-replay/117988
* json.cc (json::pointer::token::token): New ctors.
(json::pointer::token::~token): New.
(json::pointer::token::operator=): New.
(json::object::set): Set the value's m_pointer_token.
(json::array::append): Likewise.
* json.h (json::pointer::token): New struct.
(json::value::get_pointer_token): New accessor.
(json::value::m_pointer_token): New field.
* libsarifreplay.cc (get_logical_location_kind_for_json_kind):
New.
(make_logical_location_from_jv): New.
(sarif_replayer::report_problem): Set the logical location of the
diagnostic.
gcc/testsuite/ChangeLog:
PR sarif-replay/117988
* sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif: Add
expected logical location.
* sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif:
Likewise.
* sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif:
Likewise.
* sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif: Likewise.
* sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif: Likewise.
* sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif: Likewise.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
15 files changed, 211 insertions, 0 deletions
diff --git a/gcc/json.cc b/gcc/json.cc index c54401b..f3f3645 100644 --- a/gcc/json.cc +++ b/gcc/json.cc @@ -74,6 +74,52 @@ print_escaped_json_string (pretty_printer *pp, pp_character (pp, '"'); } +/* class pointer::token. */ + +pointer::token::token () +{ + m_parent = nullptr; + m_data.u_member = nullptr; + m_kind = kind::root_value; +} + +pointer::token::token (json::object &parent, const char *member) +{ + m_parent = &parent; + m_data.u_member = xstrdup (member); // ideally we'd share + m_kind = kind::object_member; +} + +pointer::token::token (json::array &parent, size_t index) +{ + m_parent = &parent; + m_data.u_index = index; + m_kind = kind::array_index; +} + +pointer::token::~token () +{ + if (m_kind == kind::object_member) + { + gcc_assert (m_data.u_member); + free (m_data.u_member); + } +} + +pointer::token & +pointer::token::operator= (pointer::token &&other) +{ + m_parent = other.m_parent; + m_data = other.m_data; + m_kind = other.m_kind; + + other.m_parent = nullptr; + other.m_data.u_member = nullptr; + other.m_kind = kind::root_value; + + return *this; +} + /* class json::value. */ /* Dump this json::value tree to OUTF. @@ -268,6 +314,8 @@ object::set (const char *key, value *v) m_map.put (owned_key, v); m_keys.safe_push (owned_key); } + + v->m_pointer_token = pointer::token (*this, key); } /* Get the json::value * for KEY. @@ -401,6 +449,7 @@ void array::append (value *v) { gcc_assert (v); + v->m_pointer_token = pointer::token (*this, m_elements.length ()); m_elements.safe_push (v); } @@ -73,6 +73,49 @@ enum kind JSON_NULL }; +namespace pointer { // json::pointer + +/* Implementation of JSON pointer (RFC 6901). */ + +/* A token within a JSON pointer, expressing the parent of a particular + JSON value, and how it is descended from that parent. + + A JSON pointer can be built as a list of these tokens. */ + +struct token +{ + enum class kind + { + root_value, + object_member, + array_index + }; + + token (); + token (json::object &parent, const char *member); + token (json::array &parent, size_t index); + token (const token &other) = delete; + token (token &&other) = delete; + + ~token (); + + token & + operator= (const token &other) = delete; + + token & + operator= (token &&other); + + json::value *m_parent; + union u + { + char *u_member; + size_t u_index; + } m_data; + enum kind m_kind; +}; + +} // namespace json::pointer + /* Base class of JSON value. */ class value @@ -88,6 +131,10 @@ class value virtual object *dyn_cast_object () { return nullptr; } static int compare (const json::value &val_a, const json::value &val_b); + + const pointer::token &get_pointer_token () const { return m_pointer_token; } + + pointer::token m_pointer_token; }; /* Subclass of value for objects: a collection of key/value pairs diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index 7c7be8d..6c79762 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -106,6 +106,82 @@ make_physical_location (libgdiagnostics::manager &mgr, return mgr.new_location_from_range (start, start, end); } +static enum diagnostic_logical_location_kind_t +get_logical_location_kind_for_json_kind (enum json::kind json_kind) +{ + switch (json_kind) + { + default: + gcc_unreachable (); + + case json::JSON_OBJECT: + return DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT; + + case json::JSON_ARRAY: + return DIAGNOSTIC_LOGICAL_LOCATION_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 DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY; + /* Perhaps this should be DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, + but then we need to more carefully track context. */ + } +} + +static libgdiagnostics::logical_location +make_logical_location_from_jv (libgdiagnostics::manager &mgr, + const json::value &jv) +{ + libgdiagnostics::logical_location parent; + const json::pointer::token &pointer_token = jv.get_pointer_token (); + + if (pointer_token.m_parent) + /* Recursively ensure that we have ancestor locations. */ + parent = make_logical_location_from_jv (mgr, + *jv.m_pointer_token.m_parent); + + std::string short_name; + std::string fully_qualified_name; + switch (pointer_token.m_kind) + { + default: + gcc_unreachable (); + + case json::pointer::token::kind::root_value: + short_name = ""; + fully_qualified_name = ""; + break; + + case json::pointer::token::kind::object_member: + short_name = pointer_token.m_data.u_member; + gcc_assert (parent.m_inner); + fully_qualified_name + = std::string (parent.get_fully_qualified_name ()) + "/" + short_name; + break; + + case json::pointer::token::kind::array_index: + short_name = std::to_string (pointer_token.m_data.u_index); + gcc_assert (parent.m_inner); + fully_qualified_name + = std::string (parent.get_fully_qualified_name ()) + "/" + short_name; + break; + } + + enum diagnostic_logical_location_kind_t kind + = get_logical_location_kind_for_json_kind (jv.get_kind ()); + + return mgr.new_logical_location (kind, + parent, + short_name.c_str (), + fully_qualified_name.c_str (), + nullptr); +} + + enum class status { ok, @@ -417,6 +493,9 @@ private: m_json_location_map.get_range_for_value (jv)); diag.set_location (loc_range); + diag.set_logical_location (make_logical_location_from_jv (m_control_mgr, + jv)); + diag.finish_va (gmsgid, args); } diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif index 4743bad..9f0b87b 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif @@ -1,6 +1,9 @@ [ null ] // { dg-error "expected a sarifLog object as the top-level value \\\[SARIF v2.1.0 §3.1\\\]" } /* { dg-begin-multiline-output "" } +In JSON array '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | [ null ] | ^~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif index c4354d4..be2316a 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif @@ -9,6 +9,9 @@ } /* { dg-begin-multiline-output "" } +In JSON object '/runs/0/results/0/message': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog" } } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif index e3eb534..98c3dc9 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif @@ -9,6 +9,9 @@ } /* { dg-begin-multiline-output "" } +In JSON object '/runs/0/results/0/message': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog", "arguments": ["quick", "brown"] } } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif index 29460e1..f816859 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif @@ -10,6 +10,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/message/text': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "before {} after" }, | ^~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif index 771bd9c..d290604 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif @@ -1,6 +1,9 @@ { } // { dg-error "expected sarifLog object to have a 'version' property \\\[SARIF v2.1.0 §3.13.2\\\]" } /* { dg-begin-multiline-output "" } +In JSON object '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { } | ^~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif index 87bfd0d..c83be50 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif @@ -1,6 +1,9 @@ { "version" : 42 } // { dg-error "15: expected sarifLog.version to be a JSON string; got JSON number \\\[SARIF v2.1.0 §3.13.2\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/version': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { "version" : 42 } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif index c5a26f0..202698e 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif @@ -2,6 +2,9 @@ "runs": 42 } // { dg-error "expected sarifLog.runs to be 'null' or an array \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/runs': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 2 | "runs": 42 } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif index f142321..a4870c1 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif @@ -1,6 +1,9 @@ { "version": "2.1.0" } // { dg-error "expected sarifLog object to have a 'runs' property \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON object '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { "version": "2.1.0" } | ^~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif index 4eeaaaa..b870959 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif @@ -2,6 +2,9 @@ "runs" : [42] } // { dg-error "expected element of sarifLog.runs array to be an object \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 2 | "runs" : [42] } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif index b5c3fbe..eb60b66 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif @@ -20,6 +20,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/level': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 12 | "level": "mostly harmless", | ^~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif index ef22614..d7baac9 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif @@ -27,6 +27,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/locations/0/logicalLocations/0/index' + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 17 | "index": 42 | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif index 7f58677..20552a3 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif @@ -20,6 +20,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/level' + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 12 | "level": "none", | ^~~~~~ { dg-end-multiline-output "" } */ |