aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2025-05-06 09:26:19 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2025-05-06 09:26:19 -0400
commit52fe9502eb153f87154cfcb6a58ab84164d7e1d9 (patch)
tree4598ea8ba7c27f0ca3c060b3d23615091aab8c6e
parent9fb44cc4823106729d086713dd022a4590703a4c (diff)
downloadgcc-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>
-rw-r--r--gcc/json.cc49
-rw-r--r--gcc/json.h47
-rw-r--r--gcc/libsarifreplay.cc79
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif3
-rw-r--r--gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif3
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);
}
diff --git a/gcc/json.h b/gcc/json.h
index d1a1553..da4da85 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -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 "" } */