public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH/RFC] json.cc: format JSON output
@ 2023-11-15  0:54 David Malcolm
  2023-12-06 17:50 ` [pushed] v2: diagnostics: prettify JSON output formats David Malcolm
  0 siblings, 1 reply; 2+ messages in thread
From: David Malcolm @ 2023-11-15  0:54 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

Previously our JSON output emitted the JSON all on one line, with
no indentation or newlines to show the structure of the values.

Although it's easy to reformat such output (e.g. with
"python -m json.tool"), I've found it's a pain to need to do so
e.g. my text editor sometimes hangs when opening a multimegabyte
json file all on one line.  Similarly diff-ing is easier if the
json is already formatted.

This patch add whitespace to json output to show the structure.
It turned out to be fairly easy to implement using pretty_printer's
existing indentation machinery, and it seems to be a quality-of-life
improvement for users.

For example, with this patch, the output from
fdiagnostics-format=json-stderr looks like:

[{"kind": "warning",
  "message": "stack-based buffer overflow",
  "option": "-Wanalyzer-out-of-bounds",
  "option_url": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-out-of-bounds",
  "children": [{"kind": "note",
                "message": "write of 350 bytes to beyond the end of ‘buf’",
                "locations": [{"caret": {"file": "../../src/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c",
                                         "line": 20,
                                         "display-column": 3,
                                         "byte-column": 3,
                                         "column": 3},
                               "finish": {"file": "../../src/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c",
                                          "line": 20,
                                          "display-column": 27,
                                          "byte-column": 27,
                                          "column": 27}}],
                "escape-source": false},
               {"kind": "note",
                "message": "valid subscripts for ‘buf’ are ‘[0]’ to ‘[99]’",
                "locations": [{"caret": {"file": "../../src/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c",
                                         "line": 20,
                                         "display-column": 3,
                                         "byte-column": 3,
                                         "column": 3},
                               "finish": {"file": "../../src/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c",
                                          "line": 20,
                                          "display-column": 27,
                                          "byte-column": 27,
                                          "column": 27}}],
                "escape-source": false}],
  "column-origin": 1,
...snip...]

I considered adding params and an option to control this formatting, but
it seems something you'd always want enabled; we already gzip some of
our json output, so it seems unlikely to affect sizes.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

Thoughts?

gcc/ChangeLog:
	* doc/invoke.texi (-fdiagnostics-format=json): Remove discussion
	about JSON output needing formatting.
	* json.cc (object::print): Add whitespace to format the JSON
	output.
	(array::print): Likewise.
	(selftest::test_writing_objects): Update expected output.
	(selftest::test_writing_arrays): Likewise.
	(selftest::test_formatting): New.
	(selftest::json_cc_tests): Call it.
	* optinfo-emit-json.cc
	(selftest::test_building_json_from_dump_calls): Update search
	string to reflect indentation.
---
 gcc/doc/invoke.texi      |  3 +-
 gcc/json.cc              | 62 +++++++++++++++++++++++++++++++++++++---
 gcc/optinfo-emit-json.cc |  3 +-
 3 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1748afdbfe0a..0c4d27bd0241 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -5712,8 +5712,7 @@ where the JSON is emitted to - with the former, the JSON is emitted to stderr,
 whereas with @samp{json-file} it is written to @file{@var{source}.gcc.json}.
 
 The emitted JSON consists of a top-level JSON array containing JSON objects
-representing the diagnostics.  The JSON is emitted as one line, without
-formatting; the examples below have been formatted for clarity.
+representing the diagnostics.
 
 Diagnostics can have child diagnostics.  For example, this error and note:
 
diff --git a/gcc/json.cc b/gcc/json.cc
index d0f157f0dfe7..d5a38c773f4d 100644
--- a/gcc/json.cc
+++ b/gcc/json.cc
@@ -66,6 +66,7 @@ void
 object::print (pretty_printer *pp) const
 {
   pp_character (pp, '{');
+  pp_indentation (pp) += 1;
 
   /* Iterate in the order that the keys were inserted.  */
   unsigned i;
@@ -73,15 +74,23 @@ object::print (pretty_printer *pp) const
   FOR_EACH_VEC_ELT (m_keys, i, key)
     {
       if (i > 0)
-	pp_string (pp, ", ");
+	{
+	  pp_string (pp, ",");
+	  pp_newline (pp);
+	  pp_indent (pp);
+	}
       map_t &mut_map = const_cast<map_t &> (m_map);
       value *value = *mut_map.get (key);
       pp_doublequote (pp);
       pp_string (pp, key); // FIXME: escaping?
       pp_doublequote (pp);
       pp_string (pp, ": ");
+      const int indent = strlen (key) + 4;
+      pp_indentation (pp) += indent;
       value->print (pp);
+      pp_indentation (pp) -= indent;
     }
+  pp_indentation (pp) -= 1;
   pp_character (pp, '}');
 }
 
@@ -183,14 +192,20 @@ void
 array::print (pretty_printer *pp) const
 {
   pp_character (pp, '[');
+  pp_indentation (pp) += 1;
   unsigned i;
   value *v;
   FOR_EACH_VEC_ELT (m_elements, i, v)
     {
       if (i)
-	pp_string (pp, ", ");
+	{
+	  pp_string (pp, ",");
+	  pp_newline (pp);
+	  pp_indent (pp);
+	}
       v->print (pp);
     }
+  pp_indentation (pp) -= 1;
   pp_character (pp, ']');
 }
 
@@ -354,7 +369,9 @@ test_writing_objects ()
   obj.set_string ("baz", "quux");
   /* This test relies on json::object writing out key/value pairs
      in key-insertion order.  */
-  ASSERT_PRINT_EQ (obj, "{\"foo\": \"bar\", \"baz\": \"quux\"}");
+  ASSERT_PRINT_EQ (obj,
+		   "{\"foo\": \"bar\",\n"
+		   " \"baz\": \"quux\"}");
 }
 
 /* Verify that JSON arrays are written correctly.  */
@@ -369,7 +386,9 @@ test_writing_arrays ()
   ASSERT_PRINT_EQ (arr, "[\"foo\"]");
 
   arr.append (new json::string ("bar"));
-  ASSERT_PRINT_EQ (arr, "[\"foo\", \"bar\"]");
+  ASSERT_PRINT_EQ (arr,
+		   "[\"foo\",\n"
+		   " \"bar\"]");
 }
 
 /* Verify that JSON numbers are written correctly.  */
@@ -424,6 +443,40 @@ test_writing_literals ()
   ASSERT_PRINT_EQ (literal (false), "false");
 }
 
+/* Verify that nested values are formatted correctly when written.  */
+
+static void
+test_formatting ()
+{
+  object obj;
+  object *child = new object;
+  object *grandchild = new object;
+
+  obj.set_string ("str", "bar");
+  obj.set ("child", child);
+  obj.set_integer ("int", 42);
+
+  child->set ("grandchild", grandchild);
+  child->set_integer ("int", 1776);
+
+  array *arr = new array;
+  for (int i = 0; i < 3; i++)
+    arr->append (new integer_number (i));
+  grandchild->set ("arr", arr);
+  grandchild->set_integer ("int", 1066);
+
+  /* This test relies on json::object writing out key/value pairs
+     in key-insertion order.  */
+  ASSERT_PRINT_EQ (obj,
+		   ("{\"str\": \"bar\",\n"
+		    " \"child\": {\"grandchild\": {\"arr\": [0,\n"
+		    "                                  1,\n"
+		    "                                  2],\n"
+		    "                          \"int\": 1066},\n"
+		    "           \"int\": 1776},\n"
+		    " \"int\": 42}"));
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -436,6 +489,7 @@ json_cc_tests ()
   test_writing_integer_numbers ();
   test_writing_strings ();
   test_writing_literals ();
+  test_formatting ();
 }
 
 } // namespace selftest
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index 11cad42a4330..1f8cb9b04e8e 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -471,7 +471,8 @@ test_building_json_from_dump_calls ()
   ASSERT_STR_CONTAINS (json_str, "impl_location");
   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
   ASSERT_STR_CONTAINS (json_str,
-		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
+		       " \"message\": [\"test of tree: \",\n"
+		       "             {\"expr\": \"0\"}]");
   delete json_obj;
 }
 
-- 
2.26.3


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-12-06 18:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-15  0:54 [PATCH/RFC] json.cc: format JSON output David Malcolm
2023-12-06 17:50 ` [pushed] v2: diagnostics: prettify JSON output formats David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).