Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r16-415-g9fb44cc4823106.

gcc/ChangeLog:
        * diagnostic-format-sarif.cc (maybe_get_sarif_kind): Add cases for
        new kinds of logical location.
        * doc/libgdiagnostics/topics/logical-locations.rst: Add new kinds
        of logical location for handling XML and JSON.
        * libgdiagnostics.cc (impl_logical_location_manager::get_kind):
        Add cases for new kinds of logical location.
        (diagnostic_text_sink::text_starter): Likewise, introducing a
        macro for this.
        (diagnostic_manager_debug_dump_logical_location): Likewise.
        * libgdiagnostics.h (enum diagnostic_logical_location_kind_t): Add
        new kinds of logical location for handling XML and JSON.
        * libsarifreplay.cc (handle_logical_location_object): Add entries
        to "kind_values" for decoding sarif logical location kinds
        relating to XML and JSON.
        * logical-location.h (enum logical_location_kind): Add new kinds
        of logical location for handling XML and JSON.

gcc/testsuite/ChangeLog:
        * libgdiagnostics.dg/test-nested-logical-locations-json-c.py: New test.
        * libgdiagnostics.dg/test-nested-logical-locations-json.c: New test.
        * sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif: New test.
        * sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif: New test.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 gcc/diagnostic-format-sarif.cc                |  27 +++
 .../topics/logical-locations.rst              |  28 +++
 gcc/libgdiagnostics.cc                        | 117 +++++++++++--
 gcc/libgdiagnostics.h                         |  18 +-
 gcc/libsarifreplay.cc                         |  27 ++-
 gcc/logical-location.h                        |  18 +-
 .../test-nested-logical-locations-json-c.py   |  79 +++++++++
 .../test-nested-logical-locations-json.c      | 165 ++++++++++++++++++
 .../2.1.0-valid/3.33.7-json-example.sarif     |  83 +++++++++
 .../2.1.0-valid/3.33.7-xml-example.sarif      |  77 ++++++++
 10 files changed, 626 insertions(+), 13 deletions(-)
 create mode 100644 
gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py
 create mode 100644 
gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c
 create mode 100644 
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif
 create mode 100644 
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif

diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 1b0743cb3c7d..454eaae4d905 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -2734,6 +2734,7 @@ maybe_get_sarif_kind (enum logical_location_kind kind)
     case LOGICAL_LOCATION_KIND_UNKNOWN:
       return nullptr;
 
+    /* Kinds within executable code.  */
     case LOGICAL_LOCATION_KIND_FUNCTION:
       return "function";
     case LOGICAL_LOCATION_KIND_MEMBER:
@@ -2750,6 +2751,32 @@ maybe_get_sarif_kind (enum logical_location_kind kind)
       return "parameter";
     case LOGICAL_LOCATION_KIND_VARIABLE:
       return "variable";
+
+    /* Kinds within XML or HTML documents.  */
+    case LOGICAL_LOCATION_KIND_ELEMENT:
+      return "element";
+    case LOGICAL_LOCATION_KIND_ATTRIBUTE:
+      return "attribute";
+    case LOGICAL_LOCATION_KIND_TEXT:
+      return "text";
+    case LOGICAL_LOCATION_KIND_COMMENT:
+      return "comment";
+    case LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION:
+      return "processingInstruction";
+    case LOGICAL_LOCATION_KIND_DTD:
+      return "dtd";
+    case LOGICAL_LOCATION_KIND_DECLARATION:
+      return "declaration";
+
+    /* Kinds within JSON documents.  */
+    case LOGICAL_LOCATION_KIND_OBJECT:
+      return "object";
+    case LOGICAL_LOCATION_KIND_ARRAY:
+      return "array";
+    case LOGICAL_LOCATION_KIND_PROPERTY:
+      return "property";
+    case LOGICAL_LOCATION_KIND_VALUE:
+      return "value";
     }
 }
 
diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst 
b/gcc/doc/libgdiagnostics/topics/logical-locations.rst
index 85f239d6bb1e..184b56381910 100644
--- a/gcc/doc/libgdiagnostics/topics/logical-locations.rst
+++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst
@@ -51,6 +51,8 @@ source location
       This roughly corresponds to the ``kind`` property in SARIF v2.1.0
       (`ยง3.33.7 
<https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790976>`_).
 
+      Kinds within executable code:
+
       .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION
 
       .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER
@@ -67,6 +69,32 @@ source location
 
       .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
 
+      Kinds within XML or HTML documents:
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION
+
+      Kinds within JSON documents:
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY
+
+      .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE
+
    ``parent`` can be NULL; if non-NULL it can be used to express tree-like
    nesting of logical locations, such as in::
 
diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc
index 39deb47248e1..70b0f36e0438 100644
--- a/gcc/libgdiagnostics.cc
+++ b/gcc/libgdiagnostics.cc
@@ -305,6 +305,7 @@ public:
       {
       default:
        gcc_unreachable ();
+
       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION:
        return LOGICAL_LOCATION_KIND_FUNCTION;
       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER:
@@ -321,6 +322,30 @@ public:
        return LOGICAL_LOCATION_KIND_PARAMETER;
       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE:
        return LOGICAL_LOCATION_KIND_VARIABLE;
+
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT:
+       return LOGICAL_LOCATION_KIND_ELEMENT;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE:
+       return LOGICAL_LOCATION_KIND_ATTRIBUTE;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT:
+       return LOGICAL_LOCATION_KIND_TEXT;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT:
+       return LOGICAL_LOCATION_KIND_COMMENT;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION:
+       return LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD:
+       return LOGICAL_LOCATION_KIND_DTD;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION:
+       return LOGICAL_LOCATION_KIND_DECLARATION;
+
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT:
+         return LOGICAL_LOCATION_KIND_OBJECT;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY:
+       return LOGICAL_LOCATION_KIND_ARRAY;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY:
+       return LOGICAL_LOCATION_KIND_PROPERTY;
+      case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE:
+       return LOGICAL_LOCATION_KIND_VALUE;
       }
   }
 
@@ -1097,21 +1122,57 @@ diagnostic_text_sink::text_starter 
(diagnostic_text_output_format &text_output,
   if (diag_logical_loc && diag_logical_loc != mgr.get_prev_diag_logical_loc ())
     {
       pp_set_prefix (pp, nullptr);
+
+      /* This macro is used to ensure that all format strings are visible to 
gettext
+        and checked at compile time.  */
+
+#define CASE(KIND, MSGID) \
+       case KIND:                                                      \
+         if (const char *name                                          \
+             = diag_logical_loc->m_fully_qualified_name.get_str ())    \
+           {                                                           \
+            pp_printf (pp, (MSGID), name);                             \
+            pp_character (pp, ':');                                    \
+            pp_newline (pp);                                           \
+           }                                                           \
+         break;
+
       switch (diag_logical_loc->m_kind)
        {
        default:
          break;
-       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION:
-         if (const char *name
-             = diag_logical_loc->m_fully_qualified_name.get_str ())
-           {
-             pp_printf (pp, _("In function %qs"), name);
-             pp_character (pp, ':');
-             pp_newline (pp);
-           }
-         break;
-         // TODO: handle other cases
+
+       /* Kinds within executable code.  */
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, _("In function %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER, _("In member %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE, _("In module %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE, _("In namespace %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE, _("In type %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE,
+            _("In return type %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER, _("In parameter %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE, _("In variable %qs"))
+
+       /* Kinds within XML or HTML documents.  */
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT, _("In element %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE, _("In attribute %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT, _("In text %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT, _("In comment %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION,
+            _("In processing instruction %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD, _("In DTD %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION,
+            _("In declaration %qs"))
+
+       /* Kinds within JSON documents.  */
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT, _("In JSON object %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, _("In JSON array %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY,
+            _("In JSON property %qs"))
+       CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, _("In JSON value %qs"))
        }
+
+#undef CASE
     }
   pp_set_prefix (pp,
                 text_output.build_prefix (*info));
@@ -1515,6 +1576,7 @@ diagnostic_manager_debug_dump_logical_location (const 
diagnostic_manager *diag_m
        {
        default:
          gcc_unreachable ();
+
        case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION:
          fprintf (out, "function");
          break;
@@ -1539,6 +1601,41 @@ diagnostic_manager_debug_dump_logical_location (const 
diagnostic_manager *diag_m
        case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE:
          fprintf (out, "variable");
          break;
+
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT:
+         fprintf (out, "element");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE:
+         fprintf (out, "attribute");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT:
+         fprintf (out, "text");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT:
+         fprintf (out, "comment");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION:
+         fprintf (out, "processing_instruction");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD:
+         fprintf (out, "dtd");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION:
+         fprintf (out, "declaration");
+         break;
+
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT:
+         fprintf (out, "object");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY:
+         fprintf (out, "array");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY:
+         fprintf (out, "property");
+         break;
+       case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE:
+         fprintf (out, "value");
+         break;
        }
       if (auto parent = loc->m_parent)
        {
diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h
index 14567a523eb7..f957779604bd 100644
--- a/gcc/libgdiagnostics.h
+++ b/gcc/libgdiagnostics.h
@@ -161,6 +161,7 @@ typedef struct diagnostic_logical_location 
diagnostic_logical_location;
 
 enum diagnostic_logical_location_kind_t
 {
+ /* Kinds within executable code.  */
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER,
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE,
@@ -168,7 +169,22 @@ enum diagnostic_logical_location_kind_t
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE,
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE,
   DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER,
-  DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE,
+
+  /* Kinds within XML or HTML documents.  */
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION,
+
+  /* Kinds within JSON documents.  */
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY,
+  DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE
 };
 
 /* A "diagnostic" is an opaque bundle of state for a particular
diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index f5f1f2041b43..7c7be8d98642 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -2008,7 +2008,32 @@ handle_logical_location_object (const json::object 
&logical_loc_obj,
            { "parameter",
              DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER },
            { "variable",
-             DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE } };
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE },
+
+           { "element",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT },
+           { "attribute",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE },
+           { "text",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT },
+           { "comment",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT },
+           { "processingInstruction",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION },
+           { "dtd",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD },
+           { "declaration",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION },
+
+           { "object",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT },
+           { "array",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY },
+           { "property",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY },
+           { "value",
+             DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE },
+      };
       auto result
        = get_value_from_json_string<enum diagnostic_logical_location_kind_t>
            (*kind_str, kind_prop, kind_values, ARRAY_SIZE (kind_values));
diff --git a/gcc/logical-location.h b/gcc/logical-location.h
index 4374b096fb05..dba9dc4010bf 100644
--- a/gcc/logical-location.h
+++ b/gcc/logical-location.h
@@ -33,6 +33,7 @@ enum logical_location_kind
 {
   LOGICAL_LOCATION_KIND_UNKNOWN,
 
+  /* Kinds within executable code.  */
   LOGICAL_LOCATION_KIND_FUNCTION,
   LOGICAL_LOCATION_KIND_MEMBER,
   LOGICAL_LOCATION_KIND_MODULE,
@@ -40,7 +41,22 @@ enum logical_location_kind
   LOGICAL_LOCATION_KIND_TYPE,
   LOGICAL_LOCATION_KIND_RETURN_TYPE,
   LOGICAL_LOCATION_KIND_PARAMETER,
-  LOGICAL_LOCATION_KIND_VARIABLE
+  LOGICAL_LOCATION_KIND_VARIABLE,
+
+  /* Kinds within XML or HTML documents.  */
+  LOGICAL_LOCATION_KIND_ELEMENT,
+  LOGICAL_LOCATION_KIND_ATTRIBUTE,
+  LOGICAL_LOCATION_KIND_TEXT,
+  LOGICAL_LOCATION_KIND_COMMENT,
+  LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION,
+  LOGICAL_LOCATION_KIND_DTD,
+  LOGICAL_LOCATION_KIND_DECLARATION,
+
+  /* Kinds within JSON documents.  */
+  LOGICAL_LOCATION_KIND_OBJECT,
+  LOGICAL_LOCATION_KIND_ARRAY,
+  LOGICAL_LOCATION_KIND_PROPERTY,
+  LOGICAL_LOCATION_KIND_VALUE
 };
 
 /* We want to efficiently support passing around logical locations in the
diff --git 
a/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py 
b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py
new file mode 100644
index 000000000000..39cc1a9cb0b9
--- /dev/null
+++ b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py
@@ -0,0 +1,79 @@
+from sarif import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def sarif():
+    return sarif_from_env()
+
+def test_sarif_output_with_logical_location(sarif):
+    schema = sarif['$schema']
+    assert schema == 
'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json'
+
+    version = sarif['version']
+    assert version == '2.1.0'
+
+    runs = sarif['runs']
+    run = runs[0]
+
+    tool = run['tool']
+    assert tool['driver']['name'] == 'test-nested-logical-locations-json.c.exe'
+
+    results = run['results']
+    assert len(results) == 2
+
+    result = results[0]
+    assert result['ruleId'] == 'warning'
+    assert result['level'] == 'warning'
+    assert result['message']['text'] == "product ID is blank"
+    assert len(result['locations']) == 1
+    location = result['locations'][0]
+    assert len(location['logicalLocations']) == 1
+    logical_loc = location['logicalLocations'][0]
+    assert logical_loc['index'] == 3
+    assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds/1'
+
+    result = results[1]
+    assert result['ruleId'] == 'warning'
+    assert result['level'] == 'warning'
+    assert result['message']['text'] == "value is negative"
+    assert len(result['locations']) == 1
+    location = result['locations'][0]
+    assert len(location['logicalLocations']) == 1
+    logical_loc = location['logicalLocations'][0]
+    assert logical_loc['index'] == 4
+    assert logical_loc['fullyQualifiedName'] == '/orders/0/total'
+
+    # Check theRun.logicalLocations
+    assert 'logicalLocations' in run
+    assert len(run['logicalLocations']) == 5
+    logical_loc = run['logicalLocations'][0]
+    assert logical_loc['name'] == 'orders'
+    assert logical_loc['fullyQualifiedName'] == '/orders'
+    assert logical_loc['kind'] == 'array'
+    assert logical_loc['index'] == 0
+    logical_loc = run['logicalLocations'][1]
+    assert logical_loc['name'] == '0'
+    assert logical_loc['fullyQualifiedName'] == '/orders/0'
+    assert logical_loc['kind'] == 'object'
+    assert logical_loc['parentIndex'] == 0
+    assert logical_loc['index'] == 1
+    logical_loc = run['logicalLocations'][2]
+    assert logical_loc['name'] == 'productIds'
+    assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds'
+    assert logical_loc['kind'] == 'array'
+    assert logical_loc['parentIndex'] == 1
+    assert logical_loc['index'] == 2
+    logical_loc = run['logicalLocations'][3]
+    assert logical_loc['name'] == '1'
+    assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds/1'
+    assert logical_loc['kind'] == 'value'
+    assert logical_loc['parentIndex'] == 2
+    assert logical_loc['index'] == 3
+    logical_loc = run['logicalLocations'][4]
+    assert logical_loc['name'] == 'total'
+    assert logical_loc['fullyQualifiedName'] == '/orders/0/total'
+    assert logical_loc['kind'] == 'property'
+    assert logical_loc['parentIndex'] == 1
+    assert logical_loc['index'] == 4
+
diff --git 
a/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c 
b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c
new file mode 100644
index 000000000000..d06450e1349c
--- /dev/null
+++ b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c
@@ -0,0 +1,165 @@
+/* Example of nested logical locations, based on the JSON example in
+   SARIF v2.1.0, 3.33.7 "kind" property;
+   though see https://github.com/oasis-tcs/sarif-spec/issues/670
+
+   Intended output is similar to:
+
+In JSON value '/orders/0/productIds/1':
+PATH/test-nested-logical-locations-json.c:28:32: warning: product ID is blank
+   28 |       "productIds": [ "A-101", "", "A-223" ],
+      |                                ^~
+In JSON property '/orders/0/total':
+PATH/test-nested-logical-locations-json.c:29:16: warning: value is negative
+   29 |       "total": "-3.25"
+      |                ^~~~~~~
+
+   along with the equivalent in SARIF, capturing JSON structure
+   as nested logical locations.  */
+
+#include "libgdiagnostics.h"
+#include "test-helpers.h"
+
+/* Placeholder source:
+_________1111111111222222222233333333334444444444
+1234567890123456789012345678901234567890123456789
+{
+  "orders": [
+    {
+      "productIds": [ "A-101", "", "A-223" ],
+      "total": "-3.25"
+    }
+  ]
+}
+*/
+const int start_line_num = __LINE__ - 9;
+const int line_num_of_product_ids = start_line_num + 3;
+const int line_num_of_total = line_num_of_product_ids + 1;
+
+#include <assert.h>
+
+int
+main ()
+{
+  begin_test ("test-nested-logical-locations-json.c.exe",
+             "test-nested-logical-locations-json.c.sarif",
+             __FILE__, "c");
+
+  /* Create tree of logical locations.  */
+  /* begin quoted source */
+  const diagnostic_logical_location *logical_loc_orders_arr
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY,
+                                              NULL, /* parent */
+                                              "orders",
+                                              "/orders",
+                                              NULL);
+  const diagnostic_logical_location *logical_loc_order_0
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT,
+                                              logical_loc_orders_arr, /* 
parent */
+                                              "0",
+                                              "/orders/0",
+                                              NULL);
+  const diagnostic_logical_location *logical_loc_product_ids
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY,
+                                              logical_loc_order_0, /* parent */
+                                              "productIds",
+                                              "/orders/0/productIds",
+                                              NULL);
+  const diagnostic_logical_location *logical_loc_element_1
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE,
+                                              logical_loc_product_ids, /* 
parent */
+                                              "1",
+                                              "/orders/0/productIds/1",
+                                              NULL);
+  const diagnostic_logical_location *logical_loc_total
+    = diagnostic_manager_new_logical_location (diag_mgr,
+                                              
DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY,
+                                              logical_loc_order_0, /* parent */
+                                              "total",
+                                              "/orders/0/total",
+                                              NULL);
+  /* end quoted source */
+
+  {
+    const int line_num = line_num_of_product_ids;
+    const diagnostic_physical_location *loc_start
+      = diagnostic_manager_new_location_from_file_line_column (diag_mgr,
+                                                              main_file,
+                                                              line_num,
+                                                              32);
+    const diagnostic_physical_location *loc_end
+      = diagnostic_manager_new_location_from_file_line_column (diag_mgr,
+                                                              main_file,
+                                                              line_num,
+                                                              33);
+    const diagnostic_physical_location *loc_range
+      = diagnostic_manager_new_location_from_range (diag_mgr,
+                                                   loc_start,
+                                                   loc_start,
+                                                   loc_end);
+
+    diagnostic *d = diagnostic_begin (diag_mgr,
+                                     DIAGNOSTIC_LEVEL_WARNING);
+    diagnostic_set_location (d, loc_range);
+
+    diagnostic_set_logical_location (d, logical_loc_element_1);
+
+    diagnostic_finish (d, "product ID is blank");
+  }
+  {
+    const int line_num = line_num_of_total;
+    const diagnostic_physical_location *loc_start
+      = diagnostic_manager_new_location_from_file_line_column (diag_mgr,
+                                                              main_file,
+                                                              line_num,
+                                                              16);
+    const diagnostic_physical_location *loc_end
+      = diagnostic_manager_new_location_from_file_line_column (diag_mgr,
+                                                              main_file,
+                                                              line_num,
+                                                              22);
+    const diagnostic_physical_location *loc_range
+      = diagnostic_manager_new_location_from_range (diag_mgr,
+                                                   loc_start,
+                                                   loc_start,
+                                                   loc_end);
+
+    diagnostic *d = diagnostic_begin (diag_mgr,
+                                     DIAGNOSTIC_LEVEL_WARNING);
+    diagnostic_set_location (d, loc_range);
+
+    diagnostic_set_logical_location (d, logical_loc_total);
+
+    diagnostic_finish (d, "value is negative");
+  }
+
+  return end_test ();
+}
+
+/* Check the output from the text sink.  */
+/* { dg-begin-multiline-output "" }
+In JSON value '/orders/0/productIds/1':
+   { dg-end-multiline-output "" } */
+/* { dg-regexp "\[^\n\r\]+test-nested-logical-locations-json.c:28:32: warning: 
product ID is blank" } */
+/* { dg-begin-multiline-output "" }
+   28 |       "productIds": [ "A-101", "", "A-223" ],
+      |                                ^~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+In JSON property '/orders/0/total':
+   { dg-end-multiline-output "" } */
+/* { dg-regexp "\[^\n\r\]+test-nested-logical-locations-json.c:29:16: warning: 
value is negative" } */
+/* { dg-begin-multiline-output "" }
+   29 |       "total": "-3.25"
+      |                ^~~~~~~
+   { dg-end-multiline-output "" } */
+
+/* Verify that some JSON was written to a file with the expected name:
+   { dg-final { verify-sarif-file } } */
+
+/* Use a Python script to verify various properties about the generated
+   .sarif file:
+   { dg-final { run-sarif-pytest test-nested-logical-locations-json.c 
"test-nested-logical-locations-json-c.py" } } */
diff --git 
a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif
new file mode 100644
index 000000000000..6e4cf5b38330
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif
@@ -0,0 +1,83 @@
+/* Adapted from the JSON example in SARIF v2.1.0, 3.33.7 "kind" property;
+   see https://github.com/oasis-tcs/sarif-spec/issues/670  */
+{
+    "version": "2.1.0",
+    "runs": [
+       {
+           "tool": {
+               "driver": {
+                   "name": "example JSON scanner"
+               }
+           },
+           "results": [
+               {
+                   "message": {"text": "product ID is blank"},
+                   "locations": [
+                       {
+                           "logicalLocations": [
+                               {
+                                   "fullyQualifiedName": 
"/orders/0/productIds/1",
+                                   "index": 3
+                               }
+                           ]
+                       }
+                   ]
+               },
+               {
+                   "message": {"text": "value is negative"},
+                   "locations": [
+                       {
+                           "logicalLocations": [
+                               {
+                                   "fullyQualifiedName": "/orders/0/total",
+                                   "index": 4
+                               }
+                           ]
+                       }
+                   ]
+               }
+           ],
+           "logicalLocations": [
+               {
+                   "name": "orders",
+                   "fullyQualifiedName": "/orders",
+                   "kind": "array"
+               },
+               {
+                   "name": "0",
+                   "fullyQualifiedName": "/orders/0",
+                   "kind": "object",
+                   "parentIndex": 0
+               },
+               {
+                   "name": "productIds",
+                   "fullyQualifiedName": "/orders/0/productIds",
+                   "kind": "array",
+                   "parentIndex": 1
+               },
+               {
+                   "name": "1",
+                   "fullyQualifiedName": "/orders/0/productIds/1",
+                   "kind": "value",
+                   "parentIndex": 2
+               },
+               {
+                   "name": "total",
+                   "fullyQualifiedName": "/orders/0/total",
+                   "kind": "property",
+                   "parentIndex": 1
+               }
+           ]
+       }
+    ]
+}
+
+/* { dg-begin-multiline-output "" }
+In JSON value '/orders/0/productIds/1':
+example JSON scanner: warning: product ID is blank
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+In JSON property '/orders/0/total':
+example JSON scanner: warning: value is negative
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif
new file mode 100644
index 000000000000..36afc6592916
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif
@@ -0,0 +1,77 @@
+/* Adapted from the XML example in SARIF v2.1.0, 3.33.7 "kind" property;
+   see also https://github.com/oasis-tcs/sarif-spec/issues/669  */
+{
+    "version": "2.1.0",
+    "runs": [
+       {
+           "tool": {
+               "driver": {
+                   "name": "example XML scanner"
+               }
+           },
+           "results": [
+               {
+                   "message": {"text": "empty value"},
+                   "locations": [
+                       {
+                           "logicalLocations": [
+                               {
+                                   "fullyQualifiedName": 
"/orders/order[1]/@number",
+                                   "index": 2
+                               }
+                           ]
+                       }
+                   ]
+               },
+               {
+                   "message": {"text": "total is negative"},
+                   "locations": [
+                       {
+                           "logicalLocations": [
+                               {
+                                   "fullyQualifiedName": 
"/orders/order[1]/total/text()",
+                                   "index": 3
+                               }
+                           ]
+                       }
+                   ]
+               }
+           ],
+           "logicalLocations": [
+               {
+                   "name": "orders",
+                   "fullyQualifiedName": "/orders",
+                   "kind": "element"
+               },
+               {
+                   "name": "order[1]",
+                   "fullyQualifiedName": "/orders/order[1]",
+                   "kind": "element",
+                   "parentIndex": 0
+               },
+               {
+                   "name": "number",
+                   "fullyQualifiedName": "/orders/order[1]/@number",
+                   "kind": "attribute",
+                   "parentIndex": 1
+               },
+               {
+                   "name": "text",
+                   "fullyQualifiedName": "/orders/order[1]/total/text()",
+                   "kind": "text",
+                   "parentIndex": 1
+               }
+           ]
+       }
+    ]
+}
+
+/* { dg-begin-multiline-output "" }
+In attribute '/orders/order[1]/@number':
+example XML scanner: warning: empty value
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+In text '/orders/order[1]/total/text()':
+example XML scanner: warning: total is negative
+   { dg-end-multiline-output "" } */
-- 
2.26.3

Reply via email to