From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 4BEBA3858D32 for ; Wed, 15 Nov 2023 00:55:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4BEBA3858D32 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4BEBA3858D32 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700009702; cv=none; b=jjBmEJ0Z6BUl2QF5Ufkl9gaWfVKgI23Ldf6/Gv5+8MxTBhdG24HwiVnLRA57lhZPZkOZVRaxyxp1vEJL+n8xQPMBm4woApPuZzPcg07oRmJfraxOOZL5VeQTBADK1XmuL8UvRxbzmW+0GjeNRLWoBPDUMb7sFSUGSHVVt3+RBcQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700009702; c=relaxed/simple; bh=6jiF0ARuorSZdecViwJzEbJlbhz9/zMPC4iqdSrIuwo=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Q3fm3v0VQ+Hr7KNvwJuTquCdXYdELzalk/38Ht6ZxaATmRWPUjmFCBMWUJBzQNhg3DNJd/h+cijQoNCdvuHPy+wR6R0C7VIJre6W6oF7jj52nOFWcaW//ndDY0Vtthtppb16mGIdro0tETNfK6h+FZkN4xcyS506Meyn6UkGwd8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700009700; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=86LB7UIAOsu7wVTyFCZnZoHEsQqE2dbNt9ebof06rm4=; b=WuCTKOtQ3hNkC/QJkxOjmMpxhg5M5hnnmSV1twxj8fx06nm+yevR0Sbq/wL6iGxerTQNAY 5+XRk8M6E6OcPxxAjX6owUC/yEb8X+sPFBCazZkfzBnNQZP3fcvIya0uEpSoUjxkcZjZaJ 6pLqAoXb6BHegO21mCWcx1Abl1B23r4= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-306-jhhKg46zMuaFhcX-ZoNXUQ-1; Tue, 14 Nov 2023 19:54:58 -0500 X-MC-Unique: jhhKg46zMuaFhcX-ZoNXUQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 534E28007B3 for ; Wed, 15 Nov 2023 00:54:58 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.10.115]) by smtp.corp.redhat.com (Postfix) with ESMTP id 284DF2026D4C; Wed, 15 Nov 2023 00:54:58 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH/RFC] json.cc: format JSON output Date: Tue, 14 Nov 2023 19:54:57 -0500 Message-Id: <20231115005457.3748674-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 (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