(END_UNICHAR_IDX), (END_LINE), (END_COLUMN), \
(EXPECTED_MSG))
+/* Implementation detail of ASSERT_JSON_POINTER_EQ. */
+
+static void
+assert_json_pointer_eq (const location &loc,
+ const json::value *jv,
+ const char *expected_str)
+{
+ pretty_printer pp;
+ ASSERT_TRUE_AT (loc, jv);
+ jv->print_pointer (&pp);
+ ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
+}
+
+/* Assert that JV is a non-NULL json::value *, and that
+ jv->print_pointer prints EXPECTED_STR. */
+
+#define ASSERT_JSON_POINTER_EQ(JV, EXPECTED_STR) \
+ assert_json_pointer_eq ((SELFTEST_LOCATION), (JV), (EXPECTED_STR))
+
/* Verify that the JSON lexer works as expected. */
static void
0, line_1, 0,
32, line_1, 32);
const json::object *jo = static_cast <const json::object *> (jv);
+ ASSERT_JSON_POINTER_EQ (jv, "");
json::value *foo_value = jo->get ("foo");
ASSERT_NE (foo_value, nullptr);
ASSERT_RANGE_EQ (*range,
8, line_1, 8,
12, line_1, 12);
+ ASSERT_JSON_POINTER_EQ (foo_value, "/foo");
json::value *baz_value = jo->get ("baz");
ASSERT_NE (baz_value, nullptr);
ASSERT_RANGE_EQ (*range,
22, line_1, 22,
31, line_1, 31);
+ ASSERT_JSON_POINTER_EQ (baz_value, "/baz");
json::array *baz_array = as_a <json::array *> (baz_value);
ASSERT_EQ (baz_array->length (), 2);
ASSERT_RANGE_EQ (*range,
23, line_1, 23,
24, line_1, 24);
+ ASSERT_JSON_POINTER_EQ (element0, "/baz/0");
json::value *element1 = baz_array->get (1);
ASSERT_EQ (element1->get_kind (), JSON_NULL);
ASSERT_RANGE_EQ (*range,
27, line_1, 27,
30, line_1, 30);
+ ASSERT_JSON_POINTER_EQ (element1, "/baz/1");
}
/* Verify that the JSON literals "true", "false" and "null" are parsed
}
}
+/* Verify that JSON Pointers are correctly escaped. */
+
+static void
+test_pointer_escaping ()
+{
+ std::unique_ptr<error> err;
+ /* Example adapted from RFC 6901 section 5. */
+ parser_testcase tc
+ (" {\n"
+ " \"a/b\": 1,\n"
+ " \"m~n\": 8\n"
+ " }\n");
+ ASSERT_EQ (tc.get_error (), nullptr);
+ const json::value *jv = tc.get_value ();
+ ASSERT_NE (jv, nullptr);
+ ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
+
+ auto jo = static_cast <const json::object *> (jv);
+ ASSERT_JSON_POINTER_EQ (jo->get ("a/b"), "/a~1b");
+ ASSERT_JSON_POINTER_EQ (jo->get ("m~n"), "/m~0n");
+}
+
/* Verify that we can parse an empty JSON string. */
static void
test_parse_jsonrpc ();
test_parse_empty_object ();
test_parsing_comments ();
+ test_pointer_escaping ();
test_error_empty_string ();
test_error_bad_token ();
test_error_object_with_missing_comma ();
return *this;
}
+/* Print this to PP as an RFC 6901 section 3 reference-token. */
+
+void
+pointer::token::print (pretty_printer *pp) const
+{
+ switch (m_kind)
+ {
+ case kind::root_value:
+ break;
+
+ case kind::object_member:
+ {
+ for (const char *ch = m_data.u_member; *ch; ++ch)
+ {
+ switch (*ch)
+ {
+ case '~':
+ pp_string (pp, "~0");
+ break;
+ case '/':
+ pp_string (pp, "~1");
+ break;
+ default:
+ pp_character (pp, *ch);
+ break;
+ }
+ }
+ }
+ break;
+
+ case kind::array_index:
+ pp_scalar (pp, HOST_SIZE_T_PRINT_UNSIGNED, m_data.u_index);
+ break;
+ }
+}
+
/* class json::value. */
/* Dump this json::value tree to OUTF.
}
}
+/* Print this value's JSON Pointer to PP. */
+
+void
+value::print_pointer (pretty_printer *pp) const
+{
+ /* Get path from this value to root. */
+ auto_vec<const pointer::token *> ancestry;
+ for (auto *iter = this; iter; iter = iter->m_pointer_token.m_parent)
+ ancestry.safe_push (&iter->m_pointer_token);
+
+ /* Walk backward, going from root to this value. */
+ ancestry.reverse ();
+ bool first = true;
+ for (auto iter : ancestry)
+ {
+ if (first)
+ first = false;
+ else
+ pp_character (pp, '/');
+ iter->print (pp);
+ }
+}
+
/* class json::object, a subclass of json::value, representing
an ordered collection of key/value pairs. */
#include "sarif-spec-urls.def"
#include "libsarifreplay.h"
#include "label-text.h"
+#include "pretty-print.h"
namespace {
parent = make_logical_location_from_jv (mgr,
*jv.m_pointer_token.m_parent);
- std::string short_name;
+ pretty_printer pp;
+ pointer_token.print (&pp);
+ std::string short_name = pp_formatted_text (&pp);
std::string fully_qualified_name;
switch (pointer_token.m_kind)
{
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;