Handle embedded links in plain text messages.  For now, merely
use the link text and discard the destination.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-6286-g7f4f49687b1f1b.

gcc/ChangeLog:
        * libsarifreplay.cc (struct embedded_link): New.
        (maybe_consume_embedded_link): New.
        (sarif_replayer::make_plain_text_within_result_message): Handle
        embedded links by using the link text, for now.

gcc/testsuite/ChangeLog:
        * sarif-replay.dg/2.1.0-valid/3.11.6-embedded-links.sarif: New test.
        * sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif: Update
        expected output for handling the embedded links.
        * sarif-replay.dg/2.1.0-valid/spec-example-4.sarif: Likewise.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/libsarifreplay.cc                         | 91 ++++++++++++++++++-
 .../2.1.0-valid/3.11.6-embedded-links.sarif   | 25 +++++
 .../2.1.0-valid/malloc-vs-local-4.c.sarif     |  5 +-
 .../2.1.0-valid/spec-example-4.sarif          |  2 +-
 4 files changed, 117 insertions(+), 6 deletions(-)
 create mode 100644 
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.11.6-embedded-links.sarif

diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index f1374ec4fc80..074ea90f352b 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -1176,11 +1176,91 @@ maybe_consume_placeholder (const char *&iter_src, 
unsigned *out_arg_idx)
   return false;
 }
 
+struct embedded_link
+{
+  std::string text;
+  std::string destination;
+};
+
+/*  If ITER_SRC starts with an embedded link as per §3.11.6, advance ITER_SRC
+    to immediately beyond the link, and return the link.
+
+    Otherwise, leave ITER_SRC untouched and return nullptr.  */
+
+static std::unique_ptr<embedded_link>
+maybe_consume_embedded_link (const char *&iter_src)
+{
+  if (*iter_src != '[')
+    return nullptr;
+
+  /* This *might* be an embedded link.
+     See §3.11.6 ("Messages with embedded links") and
+     https://github.com/oasis-tcs/sarif-spec/issues/657 */
+
+  /* embedded link = "[", link text, "](", link destination, ")"; */
+
+  embedded_link result;
+
+  /* Try to get the link text.  */
+  const char *iter = iter_src + 1;
+  while (char ch = *(iter++))
+    {
+      if (ch == '\\')
+       {
+         char next_ch = *iter;
+         switch (next_ch)
+           {
+           case '\\':
+           case '[':
+           case ']':
+             /* escaped link character = "\" | "[" | "]";  */
+             result.text += next_ch;
+             iter++;
+             continue;
+
+           default:
+             /* Malformed link text; assume this is not an
+                embedded link.  */
+             return nullptr;
+           }
+       }
+      else if (ch == ']')
+       /* End of link text.  */
+       break;
+      else
+       result.text += ch;
+    }
+
+  if (*iter++ != '(')
+    return nullptr;
+
+  /* Try to get the link destination.  */
+  while (1)
+    {
+      char ch = *(iter++);
+      if (ch == '\0')
+       {
+         /* String ended before terminating ')'.
+            Assume this is not an embedded link.  */
+         return nullptr;
+       }
+      else if (ch == ')')
+       /* Terminator.  */
+       break;
+      else
+       result.destination += ch;
+    }
+
+  iter_src = iter;
+  return ::make_unique<embedded_link> (std::move (result));
+}
+
 /* Lookup the plain text string within a result.message (§3.27.11),
-   and substitute for any placeholders (§3.11.5).
+   and substitute for any placeholders (§3.11.5) and handle any
+   embedded links (§3.11.6).
 
    Limitations:
-   - we don't yet support embedded links
+   - we don't preserve destinations within embedded links
 
    MESSAGE_OBJ is "theMessage"
    RULE_OBJ is "theRule".  */
@@ -1255,6 +1335,13 @@ make_plain_text_within_result_message (const 
json::object *tool_component_obj,
              return label_text::borrow (nullptr);
            }
        }
+      else if (auto link = maybe_consume_embedded_link (iter_src))
+       {
+         accum += link->text;
+         /* TODO: use the destination.  */
+         /* TODO: potentially could try to convert
+            intra-sarif links into event ids.  */
+       }
       else
        {
          accum += ch;
diff --git 
a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.11.6-embedded-links.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.11.6-embedded-links.sarif
new file mode 100644
index 000000000000..bc64521716c6
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.11.6-embedded-links.sarif
@@ -0,0 +1,25 @@
+{"$schema": 
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";,
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "hand-written"}},
+           "results": [{"message": {"text": "001: this is a test"},
+                       "locations": []},
+/* { dg-begin-multiline-output "" }
+hand-written: warning: 001: this is a test
+   { dg-end-multiline-output "" } */
+
+                       /* Without the fix from 
https://github.com/oasis-tcs/sarif-spec/issues/656 */
+                       {"message": {"text": "002: Prohibited term used in 
[para\\[0\\]\\\\spans\\[2\\](1)."},
+                       "locations": []},
+/* { dg-begin-multiline-output "" }
+hand-written: warning: 002: Prohibited term used in [para\[0\]\\spans\[2\](1).
+   { dg-end-multiline-output "" } */
+                       
+                       /* With the fix from 
https://github.com/oasis-tcs/sarif-spec/issues/656 */
+                       {"message": {"text": "003: Prohibited term used in 
[para\\[0\\]\\\\spans\\[2\\]](1)."},
+                       "locations": []}
+/* { dg-begin-multiline-output "" }
+hand-written: warning: 003: Prohibited term used in para[0]\spans[2].
+   { dg-end-multiline-output "" } */
+
+]}]}
+
diff --git 
a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
index 5fd8e628b47d..55c646bb5ad2 100644
--- a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
@@ -334,7 +334,6 @@
                                                                        
"nestingLevel": 1,
                                                                        
"executionOrder": 5}]}]}]}]}]}
 // TODO: show the CWEs
-// TODO: fix URL in message
 
 /* { dg-begin-multiline-output "" }
 In function 'callee_1':
@@ -372,7 +371,7 @@ In function 'callee_1':
            |    5 |   *ptr = 42;
            |      |   ~~~~~~~~~~                    
            |      |   |
-           |      |   (7) ‘ptr’ could be NULL: unchecked value from 
[(4)](sarif:/runs/0/results/0/codeFlows/0/threadFlows/0/locations/3)
+           |      |   (7) ‘ptr’ could be NULL: unchecked value from (4)
            |
    { dg-end-multiline-output "" } */
 /* { dg-begin-multiline-output "" }
@@ -398,5 +397,5 @@ In function 'test_2':
    38 |       free (ptr);
       |       ~~~~~~~~~~~
       |       |
-      |       (5) second ‘free’ here; first ‘free’ was at 
[(4)](sarif:/runs/0/results/1/codeFlows/0/threadFlows/0/locations/3)
+      |       (5) second ‘free’ here; first ‘free’ was at (4)
    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
index 60c87314e1c8..27a0767bd0ef 100644
--- a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
@@ -748,7 +748,7 @@
 
 /* { dg-begin-multiline-output "" }
 In function 'collections::list::add':
-collections/list.h:15:9: error: Variable "ptr" was used without being 
initialized. It was declared [here](0). [C2001]
+collections/list.h:15:9: error: Variable "ptr" was used without being 
initialized. It was declared here. [C2001]
   events 1-3
 ......
    { dg-end-multiline-output "" } */
-- 
2.26.3

Reply via email to