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.
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r16-416-g52fe9502eb153f.
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 <[email protected]>
---
gcc/json.cc | 49 ++++++++++++
gcc/json.h | 47 +++++++++++
gcc/libsarifreplay.cc | 79 +++++++++++++++++++
.../2.1.0-invalid/3.1-not-an-object.sarif | 3 +
...1-missing-arguments-for-placeholders.sarif | 3 +
...ot-enough-arguments-for-placeholders.sarif | 3 +
.../3.11.5-unescaped-braces.sarif | 3 +
.../2.1.0-invalid/3.13.2-no-version.sarif | 3 +
.../3.13.2-version-not-a-string.sarif | 3 +
.../2.1.0-invalid/3.13.4-bad-runs.sarif | 3 +
.../2.1.0-invalid/3.13.4-no-runs.sarif | 3 +
.../3.13.4-non-object-in-runs.sarif | 3 +
.../2.1.0-invalid/3.27.10-bad-level.sarif | 3 +
.../3.33.3-index-out-of-range.sarif | 3 +
.../2.1.0-unhandled/3.27.10-none-level.sarif | 3 +
15 files changed, 211 insertions(+)
diff --git a/gcc/json.cc b/gcc/json.cc
index c54401bc530e..f3f364598569 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 d1a1553c1a8b..da4da852a1ed 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 7c7be8d98642..6c797624f828 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 4743bad3ba3c..9f0b87b1d270 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,5 +1,8 @@
[ 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 ]
| ^~~~~~~~
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 c4354d4ef23d..be2316a70bae 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
@@ -8,6 +8,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"
} }
|
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 e3eb53411101..98c3dc9f115c 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
@@ -8,6 +8,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"] } }
|
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 29460e19c698..f81685954985 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
@@ -9,6 +9,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" },
| ^~~~~~~~~~~~~~~~~
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 771bd9c0c05d..d2906041b090 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,5 +1,8 @@
{ } // { 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 | { }
| ^~~
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 87bfd0de7898..c83be50ba28e 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,5 +1,8 @@
{ "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 }
| ^~
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 c5a26f055164..202698eb77be 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
@@ -1,6 +1,9 @@
{ "version": "2.1.0",
"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 }
| ^~
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 f142321642c3..a4870c19296f 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,5 +1,8 @@
{ "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" }
| ^~~~~~~~~~~~~~~~~~~~~~
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 4eeaaaa7b242..b8709598b7ad 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
@@ -1,6 +1,9 @@
{ "version": "2.1.0",
"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] }
| ^~
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 b5c3fbe55c37..eb60b6696060 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
@@ -19,6 +19,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",
| ^~~~~~~~~~~~~~~~~
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 ef22614776c5..d7baac92a0d6 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
@@ -26,6 +26,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
| ^~
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 7f5867739ffb..20552a3aa117 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
@@ -19,6 +19,9 @@
]
}
+/* { dg-begin-multiline-output "" }
+In JSON property '/runs/0/results/0/level'
+ { dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
12 | "level": "none",
| ^~~~~~
--
2.26.3