Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Checked selftests under valgrind on x86_64-pc-linux-gnu.

Pushed to trunk as r15-7255-gb4bd06774ced72.

gcc/ChangeLog:
        PR other/118675
        * diagnostic-format-sarif.cc: Define INCLUDE_STRING.
        (escape_braces): New.
        (set_string_property_escaping_braces): New.
        (sarif_builder::make_message_object): Escape braces in the "text"
        property.
        (sarif_builder::make_message_object_for_diagram): Likewise, and
        for the "markdown" property.
        (sarif_builder::make_multiformat_message_string): Likewise for the
        "text" property.
        (xelftest::test_message_with_braces): New.
        (selftest::diagnostic_format_sarif_cc_tests): Call it.

gcc/testsuite/ChangeLog:
        PR other/118675
        * gcc.dg/sarif-output/bad-binary-op.py: Update expected output for
        escaping of braces in message text.
        * gcc.dg/sarif-output/missing-semicolon.py: Likewise.
        * gcc.dg/sarif-output/multiple-outputs.py: Likewise.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/diagnostic-format-sarif.cc                | 68 +++++++++++++++++--
 .../gcc.dg/sarif-output/bad-binary-op.py      |  6 +-
 .../gcc.dg/sarif-output/missing-semicolon.py  |  2 +-
 .../gcc.dg/sarif-output/multiple-outputs.py   |  2 +-
 4 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 168a52fddf5b..554992bddba1 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #define INCLUDE_LIST
 #define INCLUDE_MAP
+#define INCLUDE_STRING
 #define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
@@ -2821,6 +2822,38 @@ sarif_builder::maybe_make_kinds_array 
(diagnostic_event::meaning m) const
   return kinds_arr;
 }
 
+/* In "3.11.5 Messages with placeholders":
+   "Within both plain text and formatted message strings, the characters
+   "{" and "}" SHALL be represented by the character sequences
+   "{{" and "}}" respectively."  */
+
+static std::string
+escape_braces (const char *text)
+{
+  std::string result;
+  while (char ch = *text++)
+    switch (ch)
+      {
+      case '{':
+      case '}':
+       result += ch;
+       /* Fall through.  */
+      default:
+       result += ch;
+       break;
+      }
+  return result;
+}
+
+static void
+set_string_property_escaping_braces (json::object &obj,
+                                    const char *property_name,
+                                    const char *value)
+{
+  std::string escaped (escape_braces (value));
+  obj.set_string (property_name, escaped.c_str ());
+}
+
 /* Make a "message" object (SARIF v2.1.0 section 3.11) for MSG.  */
 
 std::unique_ptr<sarif_message>
@@ -2829,7 +2862,8 @@ sarif_builder::make_message_object (const char *msg) const
   auto message_obj = ::make_unique<sarif_message> ();
 
   /* "text" property (SARIF v2.1.0 section 3.11.8).  */
-  message_obj->set_string ("text", msg);
+  set_string_property_escaping_braces (*message_obj,
+                                      "text", msg);
 
   return message_obj;
 }
@@ -2844,7 +2878,8 @@ sarif_builder::make_message_object_for_diagram (const 
diagnostic_diagram &diagra
   auto message_obj = ::make_unique<sarif_message> ();
 
   /* "text" property (SARIF v2.1.0 section 3.11.8).  */
-  message_obj->set_string ("text", diagram.get_alt_text ());
+  set_string_property_escaping_braces (*message_obj,
+                                      "text", diagram.get_alt_text ());
 
   pretty_printer *const pp = m_printer;
   char *saved_prefix = pp_take_prefix (pp);
@@ -2857,7 +2892,8 @@ sarif_builder::make_message_object_for_diagram (const 
diagnostic_diagram &diagra
   pp_set_prefix (pp, saved_prefix);
 
   /* "markdown" property (SARIF v2.1.0 section 3.11.9).  */
-  message_obj->set_string ("markdown", pp_formatted_text (pp));
+  set_string_property_escaping_braces (*message_obj,
+                                      "markdown", pp_formatted_text (pp));
 
   pp_clear_output_area (pp);
 
@@ -2873,7 +2909,8 @@ sarif_builder::make_multiformat_message_string (const 
char *msg) const
   auto message_obj = ::make_unique<sarif_multiformat_message_string> ();
 
   /* "text" property (SARIF v2.1.0 section 3.12.3).  */
-  message_obj->set_string ("text", msg);
+  set_string_property_escaping_braces (*message_obj,
+                                      "text", msg);
 
   return message_obj;
 }
@@ -4341,6 +4378,28 @@ test_message_with_embedded_link (enum sarif_version 
version)
   }
 }
 
+/* Verify that braces in messages get escaped, as per
+   3.11.5 ("Messages with placeholders").  */
+
+static void
+test_message_with_braces (enum sarif_version version)
+{
+  auto_fix_quotes fix_quotes;
+  {
+    test_sarif_diagnostic_context dc ("test.c", version);
+    rich_location richloc (line_table, UNKNOWN_LOCATION);
+    dc.report (DK_ERROR, richloc, nullptr, 0,
+              "open brace: %qs close brace: %qs",
+              "{", "}");
+    std::unique_ptr<sarif_log> log = dc.flush_to_object ();
+
+    auto message_obj = get_message_from_log (log.get ());
+    ASSERT_JSON_STRING_PROPERTY_EQ
+      (message_obj, "text",
+       "open brace: `{{' close brace: `}}'");
+  }
+}
+
 static void
 test_buffering (enum sarif_version version)
 {
@@ -4457,6 +4516,7 @@ diagnostic_format_sarif_cc_tests ()
 
       test_simple_log (version);
       test_message_with_embedded_link (version);
+      test_message_with_braces (version);
       test_buffering (version);
     }
 
diff --git a/gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py 
b/gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py
index fe139e62e417..2281c06a6c94 100644
--- a/gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py
+++ b/gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py
@@ -45,7 +45,7 @@ def test_error_location(sarif):
     assert result['level'] == 'error'
 
     assert result['message']['text'] \
-        == "invalid operands to binary + (have 'S' {aka 'struct s'} and 'T' 
{aka 'struct t'})"
+        == "invalid operands to binary + (have 'S' {{aka 'struct s'}} and 'T' 
{{aka 'struct t'}})"
     locations = result['locations']
     assert len(locations) == 1
 
@@ -63,8 +63,8 @@ def test_error_location(sarif):
     assert annotations[0]['startLine'] == EXPECTED_LINE
     assert annotations[0]['startColumn'] == 10
     assert annotations[0]['endColumn'] == 22
-    assert annotations[0]['message']['text'] == "S {aka struct s}"
+    assert annotations[0]['message']['text'] == "S {{aka struct s}}"
     assert annotations[1]['startLine'] == EXPECTED_LINE
     assert annotations[1]['startColumn'] == 25
     assert annotations[1]['endColumn'] == 37
-    assert annotations[1]['message']['text'] == "T {aka struct t}"
+    assert annotations[1]['message']['text'] == "T {{aka struct t}}"
diff --git a/gcc/testsuite/gcc.dg/sarif-output/missing-semicolon.py 
b/gcc/testsuite/gcc.dg/sarif-output/missing-semicolon.py
index 58c0a7d02cd8..a0f848387a22 100644
--- a/gcc/testsuite/gcc.dg/sarif-output/missing-semicolon.py
+++ b/gcc/testsuite/gcc.dg/sarif-output/missing-semicolon.py
@@ -43,7 +43,7 @@ def test_location_relationships(sarif):
     
     result = results[0]
     assert result['level'] == 'error'
-    assert result['message']['text'] == "expected ';' before '}' token"
+    assert result['message']['text'] == "expected ';' before '}}' token"
     locations = result['locations']
     assert len(locations) == 1
 
diff --git a/gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py 
b/gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py
index 8febfac4c7bf..4de91c2c8ef2 100644
--- a/gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py
+++ b/gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py
@@ -36,7 +36,7 @@ def test_result(sarif):
     
     result = results[0]
     assert result['level'] == 'error'
-    assert result['message']['text'] == "expected ';' before '}' token"
+    assert result['message']['text'] == "expected ';' before '}}' token"
     locations = result['locations']
     assert len(locations) == 1
 
-- 
2.26.3

Reply via email to